@@ -107,6 +107,8 @@ typedef struct execution_state
107
107
* which we free at completion. In non-returnsSet mode, this is just a child
108
108
* of the call-time context. In returnsSet mode, it is made a child of the
109
109
* 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.
110
112
*
111
113
* 3. SQLFunctionLink is a tiny struct that just holds pointers to
112
114
* the SQLFunctionHashEntry and the current SQLFunctionCache (if any).
@@ -151,6 +153,7 @@ typedef struct SQLFunctionCache
151
153
bool lazyEvalOK ; /* true if lazyEval is safe */
152
154
bool shutdown_reg ; /* true if registered shutdown callback */
153
155
bool lazyEval ; /* true if using lazyEval for result query */
156
+ bool ownSubcontext ; /* is subcontext really a separate context? */
154
157
155
158
ParamListInfo paramLI ; /* Param list representing current args */
156
159
@@ -178,6 +181,9 @@ typedef struct SQLFunctionCache
178
181
179
182
MemoryContext fcontext ; /* memory context holding this struct and all
180
183
* 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 */
181
187
} SQLFunctionCache ;
182
188
183
189
typedef SQLFunctionCache * SQLFunctionCachePtr ;
@@ -617,8 +623,8 @@ init_sql_fcache(FunctionCallInfo fcinfo, bool lazyEvalOK)
617
623
*/
618
624
pcontext = func -> returnsSet ? finfo -> fn_mcxt : CurrentMemoryContext ;
619
625
fcontext = AllocSetContextCreate (pcontext ,
620
- "SQL function execution " ,
621
- ALLOCSET_DEFAULT_SIZES );
626
+ "SQL function cache " ,
627
+ ALLOCSET_SMALL_SIZES );
622
628
623
629
oldcontext = MemoryContextSwitchTo (fcontext );
624
630
@@ -791,15 +797,24 @@ init_execution_state(SQLFunctionCachePtr fcache)
791
797
* nothing to coerce to. (XXX Frequently, the JunkFilter isn't doing
792
798
* anything very interesting, but much of this module expects it to be
793
799
* 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.
794
803
*/
795
804
if (fcache -> func -> rettype != VOIDOID )
796
805
{
797
806
TupleTableSlot * slot ;
798
807
List * resulttlist ;
799
808
MemoryContext oldcontext ;
800
809
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 );
803
818
804
819
slot = MakeSingleTupleTableSlot (NULL , & TTSOpsMinimalTuple );
805
820
@@ -1265,14 +1280,46 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
1265
1280
Assert (ActiveSnapshotSet ());
1266
1281
1267
1282
/*
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.
1274
1292
*/
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 );
1276
1323
1277
1324
/*
1278
1325
* If this query produces the function result, send its output to the
@@ -1335,8 +1382,8 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
1335
1382
bool result ;
1336
1383
MemoryContext oldcontext ;
1337
1384
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 );
1340
1387
1341
1388
if (es -> qd -> operation == CMD_UTILITY )
1342
1389
{
@@ -1375,8 +1422,8 @@ postquel_end(execution_state *es, SQLFunctionCachePtr fcache)
1375
1422
{
1376
1423
MemoryContext oldcontext ;
1377
1424
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 );
1380
1427
1381
1428
/* mark status done to ensure we don't do ExecutorEnd twice */
1382
1429
es -> status = F_EXEC_DONE ;
@@ -1394,6 +1441,11 @@ postquel_end(execution_state *es, SQLFunctionCachePtr fcache)
1394
1441
es -> qd = NULL ;
1395
1442
1396
1443
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 ;
1397
1449
}
1398
1450
1399
1451
/*
0 commit comments