Skip to content

Commit 69d3852

Browse files
tglsfdcCommitfest Bot
authored and
Commitfest Bot
committed
Split some storage out to separate subcontexts of fcontext.
Put the JunkFilter and its result slot (and thence also some subsidiary data such as the result tupledesc) into a separate subcontext "jfcontext". This doesn't accomplish a lot at this point, because we make a new JunkFilter each time through the SQL function. However, the plan is to make the fcontext live longer, and that raises the possibility that we'll need a new JunkFilter because the plan for the result-generating query changes. A separate context makes it easy to free the obsoleted data when that happens. Also, instead of always running the sub-executor in fcontext, make a separate context for it if we're doing lazy eval of a SRF, and otherwise just run it inside CurrentMemoryContext. The combination of these steps reduces the expected size of fcontext enough that we can use ALLOCSET_SMALL_SIZES.
1 parent 12abcdd commit 69d3852

File tree

1 file changed

+67
-15
lines changed

1 file changed

+67
-15
lines changed

src/backend/executor/functions.c

+67-15
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ typedef struct execution_state
107107
* which we free at completion. In non-returnsSet mode, this is just a child
108108
* of the call-time context. In returnsSet mode, it is made a child of the
109109
* FmgrInfo's fn_mcxt so that it will survive between fmgr_sql calls.
110+
* The fcontext may have subsidiary contexts jfcontext and/or subcontext,
111+
* which have somewhat shorter lifespans.
110112
*
111113
* 3. SQLFunctionLink is a tiny struct that just holds pointers to
112114
* the SQLFunctionHashEntry and the current SQLFunctionCache (if any).
@@ -151,6 +153,7 @@ typedef struct SQLFunctionCache
151153
bool lazyEvalOK; /* true if lazyEval is safe */
152154
bool shutdown_reg; /* true if registered shutdown callback */
153155
bool lazyEval; /* true if using lazyEval for result query */
156+
bool ownSubcontext; /* is subcontext really a separate context? */
154157

155158
ParamListInfo paramLI; /* Param list representing current args */
156159

@@ -178,6 +181,9 @@ typedef struct SQLFunctionCache
178181

179182
MemoryContext fcontext; /* memory context holding this struct and all
180183
* subsidiary data */
184+
MemoryContext jfcontext; /* subsidiary memory context holding
185+
* junkFilter, result slot, and related data */
186+
MemoryContext subcontext; /* subsidiary memory context for sub-executor */
181187
} SQLFunctionCache;
182188

183189
typedef SQLFunctionCache *SQLFunctionCachePtr;
@@ -617,8 +623,8 @@ init_sql_fcache(FunctionCallInfo fcinfo, bool lazyEvalOK)
617623
*/
618624
pcontext = func->returnsSet ? finfo->fn_mcxt : CurrentMemoryContext;
619625
fcontext = AllocSetContextCreate(pcontext,
620-
"SQL function execution",
621-
ALLOCSET_DEFAULT_SIZES);
626+
"SQL function cache",
627+
ALLOCSET_SMALL_SIZES);
622628

623629
oldcontext = MemoryContextSwitchTo(fcontext);
624630

@@ -791,15 +797,24 @@ init_execution_state(SQLFunctionCachePtr fcache)
791797
* nothing to coerce to. (XXX Frequently, the JunkFilter isn't doing
792798
* anything very interesting, but much of this module expects it to be
793799
* there anyway.)
800+
*
801+
* The JunkFilter, its result slot, and its tupledesc are kept in a
802+
* subsidiary memory context so that we can free them easily when needed.
794803
*/
795804
if (fcache->func->rettype != VOIDOID)
796805
{
797806
TupleTableSlot *slot;
798807
List *resulttlist;
799808
MemoryContext oldcontext;
800809

801-
/* The result slot and JunkFilter must be in the fcontext */
802-
oldcontext = MemoryContextSwitchTo(fcache->fcontext);
810+
/* Create or reset the jfcontext */
811+
if (fcache->jfcontext == NULL)
812+
fcache->jfcontext = AllocSetContextCreate(fcache->fcontext,
813+
"SQL function junkfilter",
814+
ALLOCSET_SMALL_SIZES);
815+
else
816+
MemoryContextReset(fcache->jfcontext);
817+
oldcontext = MemoryContextSwitchTo(fcache->jfcontext);
803818

804819
slot = MakeSingleTupleTableSlot(NULL, &TTSOpsMinimalTuple);
805820

@@ -1265,14 +1280,46 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
12651280
Assert(ActiveSnapshotSet());
12661281

12671282
/*
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.)
1283+
* In lazyEval mode for a SRF, we must run the sub-executor in a child of
1284+
* fcontext, so that it can survive across multiple calls to fmgr_sql.
1285+
* (XXX in the case of a long-lived FmgrInfo, this policy potentially
1286+
* causes memory leakage, but it's not very clear where we could keep
1287+
* stuff instead. Fortunately, there are few if any cases where
1288+
* set-returning functions are invoked via FmgrInfos that would outlive
1289+
* the calling query.) Otherwise, we're going to run it to completion
1290+
* before exiting fmgr_sql, so it can perfectly well run in the caller's
1291+
* context.
12741292
*/
1275-
oldcontext = MemoryContextSwitchTo(fcache->fcontext);
1293+
if (es->lazyEval && fcache->func->returnsSet)
1294+
{
1295+
fcache->subcontext = AllocSetContextCreate(fcache->fcontext,
1296+
"SQL function execution",
1297+
ALLOCSET_DEFAULT_SIZES);
1298+
fcache->ownSubcontext = true;
1299+
}
1300+
else if (es->stmt->commandType == CMD_UTILITY)
1301+
{
1302+
/*
1303+
* The code path using a sub-executor is pretty good about cleaning up
1304+
* cruft, since the executor will make its own sub-context. We don't
1305+
* really need an additional layer of sub-context in that case.
1306+
* However, if this is a utility statement, it won't make its own
1307+
* sub-context, so it seems advisable to make one that we can free on
1308+
* completion.
1309+
*/
1310+
fcache->subcontext = AllocSetContextCreate(CurrentMemoryContext,
1311+
"SQL function execution",
1312+
ALLOCSET_DEFAULT_SIZES);
1313+
fcache->ownSubcontext = true;
1314+
}
1315+
else
1316+
{
1317+
fcache->subcontext = CurrentMemoryContext;
1318+
fcache->ownSubcontext = false;
1319+
}
1320+
1321+
/* Switch into the selected subcontext (might be a no-op) */
1322+
oldcontext = MemoryContextSwitchTo(fcache->subcontext);
12761323

12771324
/*
12781325
* If this query produces the function result, send its output to the
@@ -1335,8 +1382,8 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
13351382
bool result;
13361383
MemoryContext oldcontext;
13371384

1338-
/* Run the sub-executor in a child of fcontext */
1339-
oldcontext = MemoryContextSwitchTo(fcache->fcontext);
1385+
/* Run the sub-executor in subcontext */
1386+
oldcontext = MemoryContextSwitchTo(fcache->subcontext);
13401387

13411388
if (es->qd->operation == CMD_UTILITY)
13421389
{
@@ -1375,8 +1422,8 @@ postquel_end(execution_state *es, SQLFunctionCachePtr fcache)
13751422
{
13761423
MemoryContext oldcontext;
13771424

1378-
/* Run the sub-executor in a child of fcontext */
1379-
oldcontext = MemoryContextSwitchTo(fcache->fcontext);
1425+
/* Run the sub-executor in subcontext */
1426+
oldcontext = MemoryContextSwitchTo(fcache->subcontext);
13801427

13811428
/* mark status done to ensure we don't do ExecutorEnd twice */
13821429
es->status = F_EXEC_DONE;
@@ -1394,6 +1441,11 @@ postquel_end(execution_state *es, SQLFunctionCachePtr fcache)
13941441
es->qd = NULL;
13951442

13961443
MemoryContextSwitchTo(oldcontext);
1444+
1445+
/* Delete the subcontext, if it's actually a separate context */
1446+
if (fcache->ownSubcontext)
1447+
MemoryContextDelete(fcache->subcontext);
1448+
fcache->subcontext = NULL;
13971449
}
13981450

13991451
/*

0 commit comments

Comments
 (0)