Skip to content

Commit 2709549

Browse files
committed
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
1 parent 21c90df commit 2709549

File tree

1 file changed

+22
-21
lines changed

1 file changed

+22
-21
lines changed

src/backend/executor/nodeHash.c

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls)
267267
* Initialize the hash table control block.
268268
*
269269
* The hashtable control block is just palloc'd from the executor's
270-
* per-query memory context.
270+
* per-query memory context. Everything else should be kept inside the
271+
* subsidiary hashCxt or batchCxt.
271272
*/
272273
hashtable = (HashJoinTable) palloc(sizeof(HashJoinTableData));
273274
hashtable->nbuckets = nbuckets;
@@ -294,6 +295,26 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls)
294295
hashtable->spaceAllowedSkew =
295296
hashtable->spaceAllowed * SKEW_WORK_MEM_PERCENT / 100;
296297

298+
/*
299+
* Create temporary memory contexts in which to keep the hashtable working
300+
* storage. See notes in executor/hashjoin.h.
301+
*/
302+
hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext,
303+
"HashTableContext",
304+
ALLOCSET_DEFAULT_MINSIZE,
305+
ALLOCSET_DEFAULT_INITSIZE,
306+
ALLOCSET_DEFAULT_MAXSIZE);
307+
308+
hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt,
309+
"HashBatchContext",
310+
ALLOCSET_DEFAULT_MINSIZE,
311+
ALLOCSET_DEFAULT_INITSIZE,
312+
ALLOCSET_DEFAULT_MAXSIZE);
313+
314+
/* Allocate data that will live for the life of the hashjoin */
315+
316+
oldcxt = MemoryContextSwitchTo(hashtable->hashCxt);
317+
297318
/*
298319
* Get info about the hash functions to be used for each hash key. Also
299320
* remember whether the join operators are strict.
@@ -320,26 +341,6 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls)
320341
i++;
321342
}
322343

323-
/*
324-
* Create temporary memory contexts in which to keep the hashtable working
325-
* storage. See notes in executor/hashjoin.h.
326-
*/
327-
hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext,
328-
"HashTableContext",
329-
ALLOCSET_DEFAULT_MINSIZE,
330-
ALLOCSET_DEFAULT_INITSIZE,
331-
ALLOCSET_DEFAULT_MAXSIZE);
332-
333-
hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt,
334-
"HashBatchContext",
335-
ALLOCSET_DEFAULT_MINSIZE,
336-
ALLOCSET_DEFAULT_INITSIZE,
337-
ALLOCSET_DEFAULT_MAXSIZE);
338-
339-
/* Allocate data that will live for the life of the hashjoin */
340-
341-
oldcxt = MemoryContextSwitchTo(hashtable->hashCxt);
342-
343344
if (nbatch > 1)
344345
{
345346
/*

0 commit comments

Comments
 (0)