]> granicus.if.org Git - postgresql/commitdiff
Fix query-lifespan memory leakage in repeatedly executed hash joins.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 16 Mar 2018 20:03:45 +0000 (16:03 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 16 Mar 2018 20:03:45 +0000 (16:03 -0400)
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

index d10d94ccc264a2ef6d037265796a69abb37aa101..75637d586a834bc98e3d730038594f89021a7565 100644 (file)
@@ -272,7 +272,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;
@@ -309,6 +310,22 @@ 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_SIZES);
+
+       hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt,
+                                                                                               "HashBatchContext",
+                                                                                               ALLOCSET_DEFAULT_SIZES);
+
+       /* 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.
@@ -335,22 +352,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_SIZES);
-
-       hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt,
-                                                                                               "HashBatchContext",
-                                                                                               ALLOCSET_DEFAULT_SIZES);
-
-       /* Allocate data that will live for the life of the hashjoin */
-
-       oldcxt = MemoryContextSwitchTo(hashtable->hashCxt);
-
        if (nbatch > 1)
        {
                /*