Improve plpgsql's memory management to fix some function-lifespan leaks.

In some cases, exiting out of a plpgsql statement due to an error, then
catching the error in a surrounding exception block, led to leakage of
temporary data the statement was working with, because we kept all such
data in the function-lifespan SPI Proc context.  Iterating such behavior
many times within one function call thus led to noticeable memory bloat.

To fix, create an additional memory context meant to have statement
lifespan.  Since many plpgsql statements, particularly the simpler/more
common ones, don't need this, create it only on demand.  Reset this context
at the end of any statement that uses it, and arrange for exception cleanup
to reset it too, thereby fixing the memory-leak issue.  Allow a stack of
such contexts to exist to handle cases where a compound statement needs
statement-lifespan data that persists across calls of inner statements.

While at it, clean up code and improve comments referring to the existing
short-term memory context, which by plpgsql convention is the per-tuple
context of the eval_econtext ExprContext.  We now uniformly refer to that
as the eval_mcontext, whereas the new statement-lifespan memory contexts
are called stmt_mcontext.

This change adds some context-creation overhead, but on the other hand
it allows removal of some retail pfree's in favor of context resets.
On balance it seems to be about a wash performance-wise.

In principle this is a bug fix, but it seems too invasive for a back-patch,
and the infrequency of complaints weighs against taking the risk in the
back branches.  So we'll fix it only in HEAD, at least for now.

Tom Lane, reviewed by Pavel Stehule

Discussion: <17863.1469142152@sss.pgh.pa.us>
This commit is contained in:
Tom Lane 2016-08-17 14:51:10 -04:00
parent 0921554657
commit bfaaacc805
2 changed files with 403 additions and 193 deletions

File diff suppressed because it is too large Load Diff

View File

@ -814,10 +814,14 @@ typedef struct PLpgSQL_execstate
/* EState to use for "simple" expression evaluation */
EState *simple_eval_estate;
/* Lookup table to use for executing type casts */
/* lookup table to use for executing type casts */
HTAB *cast_hash;
MemoryContext cast_hash_context;
/* memory context for statement-lifespan temporary values */
MemoryContext stmt_mcontext; /* current stmt context, or NULL if none */
MemoryContext stmt_mcontext_parent; /* parent of current context */
/* temporary state for results from evaluation of query or expr */
SPITupleTable *eval_tuptable;
uint64 eval_processed;