postgres.git / summary / log / commit / refs
commit c17a58967d97461a088ca2f19da4c46cf35e4194
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 94441f807e6..0d02a2139b1 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -275,7 +275,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;
@@ -312,6 +313,26 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls)
hashtable, nbatch, nbuckets);
#endif
+ /*
+ * 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.
@@ -338,26 +359,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: f4512fa5b3af]