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

Commit595d1ef

Browse files
committed
Make functions.c mostly run in a short-lived memory context.
Previously, much of this code ran with CurrentMemoryContext setto be the function's fcontext, so that we tended to leak a lot ofstuff there. Commit0dca5d6 dealt with that by releasing thefcontext at the completion of each SQL function call, but we'dlike to go back to the previous approach of allowing the fcontextto be query-lifespan. To control the leakage problem, rearrangethe code so that we mostly run in the memory context that fmgr_sqlis called in (which we expect to be short-lived). Notably, thismeans that parsing/planning is all done in the short-lived contextand doesn't leak cruft into fcontext.This patch also fixes the allocation of execution_state recordsso that we don't leak them across executions. I set that upwith a re-usable array that contains at least as manyexecution_state structs as we need for the current querytree.The chain structure is still there, but it's not really doingmuch for us, and maybe somebody will be motivated to get ridof it. I'm not though.This incidentally also moves the call of BlessTupleDesc to bewith the code that creates the JunkFilter. That doesn't makemuch difference now, but a later patch will reduce the numberof times the JunkFilter gets made, and we needn't bless theresults any more often than that.We still leak a fair amount in fcontext, particularly whenexecuting utility statements, but that's material for aseparate patch step; the point here is only to get rid ofunintentional allocations in fcontext.
1 parent09b07c2 commit595d1ef

File tree

1 file changed

+95
-58
lines changed

1 file changed

+95
-58
lines changed

‎src/backend/executor/functions.c‎

Lines changed: 95 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ typedef struct
5555
*
5656
* The "next" fields chain together all the execution_state records generated
5757
* from a single original parsetree. (There will only be more than one in
58-
* case of rule expansion of the original parsetree.)
58+
* case of rule expansion of the original parsetree.) The chain structure is
59+
* quite vestigial at this point, because we allocate the records in an array
60+
* for ease of memory management. But we'll get rid of it some other day.
5961
*/
6062
typedefenum
6163
{
@@ -158,17 +160,20 @@ typedef struct SQLFunctionCache
158160

159161
/*
160162
* While executing a particular query within the function, cplan is the
161-
* CachedPlan we've obtained for that query, and eslist is alist of
163+
* CachedPlan we've obtained for that query, and eslist is achain of
162164
* execution_state records for the individual plans within the CachedPlan.
163165
* next_query_index is the 0-based index of the next CachedPlanSource to
164166
* get a CachedPlan from.
165167
*/
166168
CachedPlan*cplan;/* Plan for current query, if any */
167169
ResourceOwnercowner;/* CachedPlan is registered with this owner */
168-
execution_state*eslist;/* execution_state records */
169170
intnext_query_index;/* index of next CachedPlanSource to run */
170171

171-
/* if positive, this is the index of the query we're processing */
172+
execution_state*eslist;/* chain of execution_state records */
173+
execution_state*esarray;/* storage for eslist */
174+
intesarray_len;/* allocated length of esarray[] */
175+
176+
/* if positive, this is the 1-based index of the query we're processing */
172177
interror_query_index;
173178

174179
MemoryContextfcontext;/* memory context holding this struct and all
@@ -212,13 +217,12 @@ static void sql_delete_callback(CachedFunction *cfunc);
212217
staticvoidsql_postrewrite_callback(List*querytree_list,void*arg);
213218
staticvoidpostquel_start(execution_state*es,SQLFunctionCachePtrfcache);
214219
staticboolpostquel_getnext(execution_state*es,SQLFunctionCachePtrfcache);
215-
staticvoidpostquel_end(execution_state*es);
220+
staticvoidpostquel_end(execution_state*es,SQLFunctionCachePtrfcache);
216221
staticvoidpostquel_sub_params(SQLFunctionCachePtrfcache,
217222
FunctionCallInfofcinfo);
218223
staticDatumpostquel_get_single_result(TupleTableSlot*slot,
219224
FunctionCallInfofcinfo,
220-
SQLFunctionCachePtrfcache,
221-
MemoryContextresultcontext);
225+
SQLFunctionCachePtrfcache);
222226
staticvoidsql_compile_error_callback(void*arg);
223227
staticvoidsql_exec_error_callback(void*arg);
224228
staticvoidShutdownSQLFunction(Datumarg);
@@ -658,12 +662,11 @@ init_execution_state(SQLFunctionCachePtr fcache)
658662
CachedPlanSource*plansource;
659663
execution_state*preves=NULL;
660664
execution_state*lasttages=NULL;
665+
intnstmts;
661666
ListCell*lc;
662667

663668
/*
664-
* Clean up after previous query, if there was one. Note that we just
665-
* leak the old execution_state records until end of function execution;
666-
* there aren't likely to be enough of them to matter.
669+
* Clean up after previous query, if there was one.
667670
*/
668671
if (fcache->cplan)
669672
{
@@ -704,6 +707,22 @@ init_execution_state(SQLFunctionCachePtr fcache)
704707
fcache->cowner,
705708
NULL);
706709

710+
/*
711+
* If necessary, make esarray[] bigger to hold the needed state.
712+
*/
713+
nstmts=list_length(fcache->cplan->stmt_list);
714+
if (nstmts>fcache->esarray_len)
715+
{
716+
if (fcache->esarray==NULL)
717+
fcache->esarray= (execution_state*)
718+
MemoryContextAlloc(fcache->fcontext,
719+
sizeof(execution_state)*nstmts);
720+
else
721+
fcache->esarray=repalloc_array(fcache->esarray,
722+
execution_state,nstmts);
723+
fcache->esarray_len=nstmts;
724+
}
725+
707726
/*
708727
* Build execution_state list to match the number of contained plans.
709728
*/
@@ -740,7 +759,7 @@ init_execution_state(SQLFunctionCachePtr fcache)
740759
CreateCommandName((Node*)stmt))));
741760

742761
/* OK, build the execution_state for this query */
743-
newes=(execution_state*)palloc(sizeof(execution_state));
762+
newes=&fcache->esarray[foreach_current_index(lc)];
744763
if (preves)
745764
preves->next=newes;
746765
else
@@ -775,9 +794,14 @@ init_execution_state(SQLFunctionCachePtr fcache)
775794
*/
776795
if (fcache->func->rettype!=VOIDOID)
777796
{
778-
TupleTableSlot*slot=MakeSingleTupleTableSlot(NULL,
779-
&TTSOpsMinimalTuple);
797+
TupleTableSlot*slot;
780798
List*resulttlist;
799+
MemoryContextoldcontext;
800+
801+
/* The result slot and JunkFilter must be in the fcontext */
802+
oldcontext=MemoryContextSwitchTo(fcache->fcontext);
803+
804+
slot=MakeSingleTupleTableSlot(NULL,&TTSOpsMinimalTuple);
781805

782806
/*
783807
* Re-fetch the (possibly modified) output tlist of the final
@@ -811,14 +835,17 @@ init_execution_state(SQLFunctionCachePtr fcache)
811835
* pointer.
812836
*/
813837
fcache->junkFilter->jf_targetList=NIL;
814-
}
815838

816-
if (fcache->func->returnsTuple)
817-
{
818839
/* Make sure output rowtype is properly blessed */
819-
BlessTupleDesc(fcache->junkFilter->jf_resultSlot->tts_tupleDescriptor);
840+
if (fcache->func->returnsTuple)
841+
BlessTupleDesc(fcache->junkFilter->jf_resultSlot->tts_tupleDescriptor);
842+
843+
MemoryContextSwitchTo(oldcontext);
820844
}
821-
elseif (fcache->func->returnsSet&&type_is_rowtype(fcache->func->rettype))
845+
846+
if (fcache->func->returnsSet&&
847+
!fcache->func->returnsTuple&&
848+
type_is_rowtype(fcache->func->rettype))
822849
{
823850
/*
824851
* Returning rowtype as if it were scalar --- materialize won't work.
@@ -1230,12 +1257,23 @@ static void
12301257
postquel_start(execution_state*es,SQLFunctionCachePtrfcache)
12311258
{
12321259
DestReceiver*dest;
1260+
MemoryContextoldcontext;
12331261

12341262
Assert(es->qd==NULL);
12351263

12361264
/* Caller should have ensured a suitable snapshot is active */
12371265
Assert(ActiveSnapshotSet());
12381266

1267+
/*
1268+
* Run the sub-executor in a child of fcontext. The sub-executor is
1269+
* responsible for deleting per-tuple information. (XXX in the case of a
1270+
* long-lived FmgrInfo, this policy potentially causes memory leakage, but
1271+
* it's not very clear where we could keep stuff instead. Fortunately,
1272+
* there are few if any cases where set-returning functions are invoked
1273+
* via FmgrInfos that would outlive the calling query.)
1274+
*/
1275+
oldcontext=MemoryContextSwitchTo(fcache->fcontext);
1276+
12391277
/*
12401278
* If this query produces the function result, send its output to the
12411279
* tuplestore; else discard any output.
@@ -1285,6 +1323,8 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
12851323
}
12861324

12871325
es->status=F_EXEC_RUN;
1326+
1327+
MemoryContextSwitchTo(oldcontext);
12881328
}
12891329

12901330
/* Run one execution_state; either to completion or to first result row */
@@ -1293,6 +1333,10 @@ static bool
12931333
postquel_getnext(execution_state*es,SQLFunctionCachePtrfcache)
12941334
{
12951335
boolresult;
1336+
MemoryContextoldcontext;
1337+
1338+
/* Run the sub-executor in a child of fcontext */
1339+
oldcontext=MemoryContextSwitchTo(fcache->fcontext);
12961340

12971341
if (es->qd->operation==CMD_UTILITY)
12981342
{
@@ -1320,13 +1364,20 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
13201364
result= (count==0||es->qd->estate->es_processed==0);
13211365
}
13221366

1367+
MemoryContextSwitchTo(oldcontext);
1368+
13231369
returnresult;
13241370
}
13251371

13261372
/* Shut down execution of one execution_state node */
13271373
staticvoid
1328-
postquel_end(execution_state*es)
1374+
postquel_end(execution_state*es,SQLFunctionCachePtrfcache)
13291375
{
1376+
MemoryContextoldcontext;
1377+
1378+
/* Run the sub-executor in a child of fcontext */
1379+
oldcontext=MemoryContextSwitchTo(fcache->fcontext);
1380+
13301381
/* mark status done to ensure we don't do ExecutorEnd twice */
13311382
es->status=F_EXEC_DONE;
13321383

@@ -1341,9 +1392,16 @@ postquel_end(execution_state *es)
13411392

13421393
FreeQueryDesc(es->qd);
13431394
es->qd=NULL;
1395+
1396+
MemoryContextSwitchTo(oldcontext);
13441397
}
13451398

1346-
/* Build ParamListInfo array representing current arguments */
1399+
/*
1400+
* Build ParamListInfo array representing current arguments.
1401+
*
1402+
* This must be called in the fcontext so that the results have adequate
1403+
* lifespan.
1404+
*/
13471405
staticvoid
13481406
postquel_sub_params(SQLFunctionCachePtrfcache,
13491407
FunctionCallInfofcinfo)
@@ -1398,25 +1456,22 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
13981456
/*
13991457
* Extract the SQL function's value from a single result row. This is used
14001458
* both for scalar (non-set) functions and for each row of a lazy-eval set
1401-
* result.
1459+
* result. We expect the current memory context to be that of the caller
1460+
* of fmgr_sql.
14021461
*/
14031462
staticDatum
14041463
postquel_get_single_result(TupleTableSlot*slot,
14051464
FunctionCallInfofcinfo,
1406-
SQLFunctionCachePtrfcache,
1407-
MemoryContextresultcontext)
1465+
SQLFunctionCachePtrfcache)
14081466
{
14091467
Datumvalue;
1410-
MemoryContextoldcontext;
14111468

14121469
/*
14131470
* Set up to return the function value. For pass-by-reference datatypes,
1414-
* be sure toallocate the resultin resultcontext, notthe currentmemory
1415-
*context (which has query lifespan). We can't leave the data inthe
1416-
*TupleTableSlot because we intend to clear the slotbefore returning.
1471+
* be sure tocopy the resultintothe currentcontext. We can't leave
1472+
*the data in the TupleTableSlot because we intend to clearthe slot
1473+
* before returning.
14171474
*/
1418-
oldcontext=MemoryContextSwitchTo(resultcontext);
1419-
14201475
if (fcache->func->returnsTuple)
14211476
{
14221477
/* We must return the whole tuple as a Datum. */
@@ -1427,16 +1482,14 @@ postquel_get_single_result(TupleTableSlot *slot,
14271482
{
14281483
/*
14291484
* Returning a scalar, which we have to extract from the first column
1430-
* of the SELECT result, and then copy intoresult context if needed.
1485+
* of the SELECT result, and then copy intocurrent context if needed.
14311486
*/
14321487
value=slot_getattr(slot,1,&(fcinfo->isnull));
14331488

14341489
if (!fcinfo->isnull)
14351490
value=datumCopy(value,fcache->func->typbyval,fcache->func->typlen);
14361491
}
14371492

1438-
MemoryContextSwitchTo(oldcontext);
1439-
14401493
returnvalue;
14411494
}
14421495

@@ -1450,7 +1503,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
14501503
SQLFunctionLink*flink;
14511504
ErrorContextCallbacksqlerrcontext;
14521505
MemoryContexttscontext;
1453-
MemoryContextoldcontext;
14541506
boolrandomAccess;
14551507
boollazyEvalOK;
14561508
boolpushed_snapshot;
@@ -1507,20 +1559,14 @@ fmgr_sql(PG_FUNCTION_ARGS)
15071559
* Build tuplestore to hold results, if we don't have one already. Make
15081560
* sure it's in a suitable context.
15091561
*/
1510-
oldcontext=MemoryContextSwitchTo(tscontext);
1511-
15121562
if (!fcache->tstore)
1513-
fcache->tstore=tuplestore_begin_heap(randomAccess, false,work_mem);
1563+
{
1564+
MemoryContextoldcontext;
15141565

1515-
/*
1516-
* Switch to context in which the fcache lives. The sub-executor is
1517-
* responsible for deleting per-tuple information. (XXX in the case of a
1518-
* long-lived FmgrInfo, this policy potentially causes memory leakage, but
1519-
* it's not very clear where we could keep stuff instead. Fortunately,
1520-
* there are few if any cases where set-returning functions are invoked
1521-
* via FmgrInfos that would outlive the calling query.)
1522-
*/
1523-
MemoryContextSwitchTo(fcache->fcontext);
1566+
oldcontext=MemoryContextSwitchTo(tscontext);
1567+
fcache->tstore=tuplestore_begin_heap(randomAccess, false,work_mem);
1568+
MemoryContextSwitchTo(oldcontext);
1569+
}
15241570

15251571
/*
15261572
* Find first unfinished execution_state. If none, advance to the next
@@ -1598,7 +1644,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
15981644
* don't care about fetching any more result rows.
15991645
*/
16001646
if (completed|| !fcache->func->returnsSet)
1601-
postquel_end(es);
1647+
postquel_end(es,fcache);
16021648

16031649
/*
16041650
* Break from loop if we didn't shut down (implying we got a
@@ -1657,8 +1703,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
16571703
if (!tuplestore_gettupleslot(fcache->tstore, true, false,slot))
16581704
elog(ERROR,"failed to fetch lazy-eval tuple");
16591705
/* Extract the result as a datum, and copy out from the slot */
1660-
result=postquel_get_single_result(slot,fcinfo,
1661-
fcache,oldcontext);
1706+
result=postquel_get_single_result(slot,fcinfo,fcache);
16621707
/* Clear the tuplestore, but keep it for next time */
16631708
/* NB: this might delete the slot's content, but we don't care */
16641709
tuplestore_clear(fcache->tstore);
@@ -1716,12 +1761,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
17161761
fcache->tstore=NULL;
17171762
/* must copy desc because execSRF.c will free it */
17181763
if (fcache->junkFilter)
1719-
{
1720-
/* setDesc must be allocated in suitable context */
1721-
MemoryContextSwitchTo(tscontext);
17221764
rsi->setDesc=CreateTupleDescCopy(fcache->junkFilter->jf_cleanTupType);
1723-
MemoryContextSwitchTo(fcache->fcontext);
1724-
}
17251765

17261766
fcinfo->isnull= true;
17271767
result= (Datum)0;
@@ -1746,8 +1786,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
17461786
/* Re-use the junkfilter's output slot to fetch back the tuple */
17471787
slot=fcache->junkFilter->jf_resultSlot;
17481788
if (tuplestore_gettupleslot(fcache->tstore, true, false,slot))
1749-
result=postquel_get_single_result(slot,fcinfo,
1750-
fcache,oldcontext);
1789+
result=postquel_get_single_result(slot,fcinfo,fcache);
17511790
else
17521791
{
17531792
fcinfo->isnull= true;
@@ -1770,8 +1809,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
17701809
if (pushed_snapshot)
17711810
PopActiveSnapshot();
17721811

1773-
MemoryContextSwitchTo(oldcontext);
1774-
17751812
/*
17761813
* If we've gone through every command in the function, we are done.
17771814
* Release the cache to start over again on next call.
@@ -1889,7 +1926,7 @@ ShutdownSQLFunction(Datum arg)
18891926
if (!fcache->func->readonly_func)
18901927
PushActiveSnapshot(es->qd->snapshot);
18911928

1892-
postquel_end(es);
1929+
postquel_end(es,fcache);
18931930

18941931
if (!fcache->func->readonly_func)
18951932
PopActiveSnapshot();

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp