Small improvements for allocation logic in ginHeapTupleFastCollect().
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Dec 2018 16:41:36 +0000 (11:41 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Dec 2018 16:41:36 +0000 (11:41 -0500)
Avoid repetitive calls to repalloc() when the required size of the
collector array grows more than 2x in one call.  Also ensure that the
array size is a power of 2 (since palloc will probably consume a power
of 2 anyway) and doesn't start out very small (which'd likely just lead
to extra repallocs).

David Rowley, tweaked a bit by me

Discussion: https://postgr.es/m/CAKJS1f8vn-iSBE8PKeVHrnhvyjRNYCxguPFFY08QLYmjWG9hPQ@mail.gmail.com

src/backend/access/gin/ginfast.c

index ca2a32bd257c4f49a1afac83a2cc8dbdcaee3e50..a800659b9230ade10efb15f19a826c4311b08976 100644 (file)
@@ -486,18 +486,41 @@ ginHeapTupleFastCollect(GinState *ginstate,
        entries = ginExtractEntries(ginstate, attnum, value, isNull,
                                                                &nentries, &categories);
 
+       /*
+        * Protect against integer overflow in allocation calculations
+        */
+       if (nentries < 0 ||
+               collector->ntuples + nentries > MaxAllocSize / sizeof(IndexTuple))
+               elog(ERROR, "too many entries for GIN index");
+
        /*
         * Allocate/reallocate memory for storing collected tuples
         */
        if (collector->tuples == NULL)
        {
-               collector->lentuples = nentries * ginstate->origTupdesc->natts;
+               /*
+                * Determine the number of elements to allocate in the tuples array
+                * initially.  Make it a power of 2 to avoid wasting memory when
+                * resizing (since palloc likes powers of 2).
+                */
+               collector->lentuples = 16;
+               while (collector->lentuples < nentries)
+                       collector->lentuples *= 2;
+
                collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) * collector->lentuples);
        }
-
-       while (collector->ntuples + nentries > collector->lentuples)
+       else if (collector->lentuples < collector->ntuples + nentries)
        {
-               collector->lentuples *= 2;
+               /*
+                * Advance lentuples to the next suitable power of 2.  This won't
+                * overflow, though we could get to a value that exceeds
+                * MaxAllocSize/sizeof(IndexTuple), causing an error in repalloc.
+                */
+               do
+               {
+                       collector->lentuples *= 2;
+               } while (collector->lentuples < collector->ntuples + nentries);
+
                collector->tuples = (IndexTuple *) repalloc(collector->tuples,
                                                                                                        sizeof(IndexTuple) * collector->lentuples);
        }