postgres.git / summary / log / commit / refs

commit    574386ddca69b4126e77f993d3d770c85f99d209
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Fri Mar 16 20:03:45 2018 +0000
First released in: 11.0, 9.3.23, 9.4.18, 9.5.13, 9.6.9, 10.4

    Fix query-lifespan memory leakage in repeatedly executed hash joins.
    
    ExecHashTableCreate allocated some memory that wasn't freed by
    ExecHashTableDestroy, specifically the per-hash-key function information.
    That's not a huge amount of data, but if one runs a query that repeats
    a hash join enough times, it builds up.  Fix by arranging for the data
    in question to be kept in the hashtable's hashCxt instead of leaving it
    "loose" in the query-lifespan executor context.  (This ensures that we'll
    also clean up anything that the hash functions allocate in fn_mcxt.)
    
    Per report from Amit Khandekar.  It's been like this forever, so back-patch
    to all supported branches.
    
    Discussion: https://postgr.es/m/CAJ3gD9cFofAWGvcxLOxDHC=B0hjtW8yGmUsF2hdGh97CM38=7g@mail.gmail.com


src/backend/executor/nodeHash.c | 43 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index a192fa6a8ad..1844006420e 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -267,7 +267,8 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls) * Initialize the hash table control block. * * The hashtable control block is just palloc'd from the executor's - * per-query memory context. + * per-query memory context. Everything else should be kept inside the + * subsidiary hashCxt or batchCxt. */ hashtable = (HashJoinTable) palloc(sizeof(HashJoinTableData)); hashtable->nbuckets = nbuckets; @@ -294,6 +295,26 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls) hashtable->spaceAllowedSkew = hashtable->spaceAllowed * SKEW_WORK_MEM_PERCENT / 100; + /* + * Create temporary memory contexts in which to keep the hashtable working + * storage. See notes in executor/hashjoin.h. + */ + hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext, + "HashTableContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt, + "HashBatchContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* Allocate data that will live for the life of the hashjoin */ + + oldcxt = MemoryContextSwitchTo(hashtable->hashCxt); + /* * Get info about the hash functions to be used for each hash key. Also * remember whether the join operators are strict. @@ -320,26 +341,6 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls) i++; } - /* - * Create temporary memory contexts in which to keep the hashtable working - * storage. See notes in executor/hashjoin.h. - */ - hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext, - "HashTableContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt, - "HashBatchContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* Allocate data that will live for the life of the hashjoin */ - - oldcxt = MemoryContextSwitchTo(hashtable->hashCxt); - if (nbatch > 1) { /* [parent: a9d0c862d53d]