Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 07172d5

Browse files
committed
Avoid query-lifetime memory leaks in XMLTABLE (bug #15321)
Multiple calls to XMLTABLE in a query (e.g. laterally applying it to a table with an xml column, an important use-case) were leaking large amounts of memory into the per-query context, blowing up memory usage. Repair by reorganizing memory context usage in nodeTableFuncscan; use the usual per-tuple context for row-by-row evaluations instead of perValueCxt, and use the explicitly created context -- renamed from perValueCxt to perTableCxt -- for arguments and state for each individual table-generation operation. Backpatch to PG10 where this code was introduced. Original report by IRC user begriffs; analysis and patch by me. Reviewed by Tom Lane and Pavel Stehule. Discussion: https://postgr.es/m/153394403528.10284.7530399040974170549@wrigleys.postgresql.org
1 parent 46b5e7c commit 07172d5

File tree

2 files changed

+25
-6
lines changed

2 files changed

+25
-6
lines changed

src/backend/executor/nodeTableFuncscan.c

+24-5
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
164164
/* Only XMLTABLE is supported currently */
165165
scanstate->routine = &XmlTableRoutine;
166166

167-
scanstate->perValueCxt =
167+
scanstate->perTableCxt =
168168
AllocSetContextCreate(CurrentMemoryContext,
169169
"TableFunc per value context",
170170
ALLOCSET_DEFAULT_SIZES);
@@ -282,6 +282,16 @@ tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
282282
oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
283283
tstate->tupstore = tuplestore_begin_heap(false, false, work_mem);
284284

285+
/*
286+
* Each call to fetch a new set of rows - of which there may be very many
287+
* if XMLTABLE is being used in a lateral join - will allocate a possibly
288+
* substantial amount of memory, so we cannot use the per-query context
289+
* here. perTableCxt now serves the same function as "argcontext" does in
290+
* FunctionScan - a place to store per-one-call (i.e. one result table)
291+
* lifetime data (as opposed to per-query or per-result-tuple).
292+
*/
293+
MemoryContextSwitchTo(tstate->perTableCxt);
294+
285295
PG_TRY();
286296
{
287297
routine->InitOpaque(tstate,
@@ -313,15 +323,17 @@ tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
313323
}
314324
PG_END_TRY();
315325

316-
/* return to original memory context, and clean up */
317-
MemoryContextSwitchTo(oldcxt);
326+
/* clean up and return to original memory context */
318327

319328
if (tstate->opaque != NULL)
320329
{
321330
routine->DestroyOpaque(tstate);
322331
tstate->opaque = NULL;
323332
}
324333

334+
MemoryContextSwitchTo(oldcxt);
335+
MemoryContextReset(tstate->perTableCxt);
336+
325337
return;
326338
}
327339

@@ -428,7 +440,14 @@ tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
428440

429441
ordinalitycol =
430442
((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
431-
oldcxt = MemoryContextSwitchTo(tstate->perValueCxt);
443+
444+
/*
445+
* We need a short-lived memory context that we can clean up each time
446+
* around the loop, to avoid wasting space. Our default per-tuple context
447+
* is fine for the job, since we won't have used it for anything yet in
448+
* this tuple cycle.
449+
*/
450+
oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
432451

433452
/*
434453
* Keep requesting rows from the table builder until there aren't any.
@@ -493,7 +512,7 @@ tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
493512

494513
tuplestore_putvalues(tstate->tupstore, tupdesc, values, nulls);
495514

496-
MemoryContextReset(tstate->perValueCxt);
515+
MemoryContextReset(econtext->ecxt_per_tuple_memory);
497516
}
498517

499518
MemoryContextSwitchTo(oldcxt);

src/include/nodes/execnodes.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,7 @@ typedef struct TableFuncScanState
15801580
FmgrInfo *in_functions; /* input function for each column */
15811581
Oid *typioparams; /* typioparam for each column */
15821582
int64 ordinal; /* row number to be output next */
1583-
MemoryContext perValueCxt; /* short life context for value evaluation */
1583+
MemoryContext perTableCxt; /* per-table context */
15841584
Tuplestorestate *tupstore; /* output tuple store */
15851585
} TableFuncScanState;
15861586

0 commit comments

Comments
 (0)