]> granicus.if.org Git - postgresql/commitdiff
Make the overflow guards in ExecChooseHashTableSize be more protective.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 30 Oct 2009 20:58:57 +0000 (20:58 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 30 Oct 2009 20:58:57 +0000 (20:58 +0000)
The original coding ensured nbuckets and nbatch didn't exceed INT_MAX,
which while not insane on its own terms did nothing to protect subsequent
code like "palloc(nbatch * sizeof(BufFile *))".  Since enormous join size
estimates might well be planner error rather than reality, it seems best
to constrain the initial sizes to be not more than work_mem/sizeof(pointer),
thus ensuring the allocated arrays don't exceed work_mem.  We will allow
nbatch to get bigger than that during subsequent ExecHashIncreaseNumBatches
calls, but we should still guard against integer overflow in those palloc
requests.  Per bug #5145 from Bernt Marius Johnsen.

Although the given test case only seems to fail back to 8.2, previous
releases have variants of this issue, so patch all supported branches.

src/backend/executor/nodeHash.c

index eca5f1b34d054638898e521e0c4d25a63575be80..b50cb231d198137547cbdd51707c87f1ccf4e724 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.116 2008/01/01 19:45:49 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.116.2.1 2009/10/30 20:58:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -363,6 +363,7 @@ ExecChooseHashTableSize(double ntuples, int tupwidth,
        int                     tupsize;
        double          inner_rel_bytes;
        long            hash_table_bytes;
+       long            max_pointers;
        int                     nbatch;
        int                     nbuckets;
        int                     i;
@@ -389,8 +390,13 @@ ExecChooseHashTableSize(double ntuples, int tupwidth,
        /*
         * Set nbuckets to achieve an average bucket load of NTUP_PER_BUCKET when
         * memory is filled.  Set nbatch to the smallest power of 2 that appears
-        * sufficient.
+        * sufficient.  The Min() steps limit the results so that the pointer
+        * arrays we'll try to allocate do not exceed work_mem.
         */
+       max_pointers = (work_mem * 1024L) / sizeof(void *);
+       /* also ensure we avoid integer overflow in nbatch and nbuckets */
+       max_pointers = Min(max_pointers, INT_MAX / 2);
+
        if (inner_rel_bytes > hash_table_bytes)
        {
                /* We'll need multiple batches */
@@ -399,11 +405,11 @@ ExecChooseHashTableSize(double ntuples, int tupwidth,
                int                     minbatch;
 
                lbuckets = (hash_table_bytes / tupsize) / NTUP_PER_BUCKET;
-               lbuckets = Min(lbuckets, INT_MAX / 2);
+               lbuckets = Min(lbuckets, max_pointers);
                nbuckets = (int) lbuckets;
 
                dbatch = ceil(inner_rel_bytes / hash_table_bytes);
-               dbatch = Min(dbatch, INT_MAX / 2);
+               dbatch = Min(dbatch, max_pointers);
                minbatch = (int) dbatch;
                nbatch = 2;
                while (nbatch < minbatch)
@@ -415,7 +421,7 @@ ExecChooseHashTableSize(double ntuples, int tupwidth,
                double          dbuckets;
 
                dbuckets = ceil(ntuples / NTUP_PER_BUCKET);
-               dbuckets = Min(dbuckets, INT_MAX / 2);
+               dbuckets = Min(dbuckets, max_pointers);
                nbuckets = (int) dbuckets;
 
                nbatch = 1;
@@ -489,7 +495,7 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
                return;
 
        /* safety check to avoid overflow */
-       if (oldnbatch > INT_MAX / 2)
+       if (oldnbatch > Min(INT_MAX / 2, MaxAllocSize / (sizeof(void *) * 2)))
                return;
 
        nbatch = oldnbatch * 2;