pg_freespacemap_relations
 
-       Column     |  references          | Description
-  ----------------+----------------------+------------------------------------
-   reltablespace  | pg_tablespace.oid    | Tablespace oid of the relation.
-   reldatabase    | pg_database.oid      | Database oid of the relation.
-   relfilenode    | pg_class.relfilenode | Relfilenode of the relation.
-   avgrequest     |                      | Moving average of free space 
-                  |                      | requests (NULL for indexes)
-   lastpagecount  |                      | Count of pages last reported as
-                  |                      | containing useful free space.
-   storedpages    |                      | Count of pages actually stored
-                  |                      | in free space map.
-   nextpage       |                      | Page index (from 0) to start next 
-                  |                      | search at.
+       Column       |  references          | Description
+  ------------------+----------------------+----------------------------------
+   reltablespace    | pg_tablespace.oid    | Tablespace oid of the relation.
+   reldatabase      | pg_database.oid      | Database oid of the relation.
+   relfilenode      | pg_class.relfilenode | Relfilenode of the relation.
+   avgrequest       |                      | Moving average of free space 
+                    |                      | requests (NULL for indexes)
+   interestingpages |                      | Count of pages last reported as
+                    |                      | containing useful free space.
+   storedpages      |                      | Count of pages actually stored
+                    |                      | in free space map.
+   nextpage         |                      | Page index (from 0) to start next 
+                    |                      | search at.
 
 
    pg_freespacemap_pages
 
   For pg_freespacemap_relations, there is one row for each relation in the free
   space map.  storedpages is the number of pages actually stored in the map,
-  while lastpagecount is the number of pages VACUUM last tried to store
-  (ie, the number that VACUUM thought had useful amounts of free space).
+  while interestingpages is the number of pages the last VACUUM thought had
+  useful amounts of free space.
 
-  If storedpages is consistently less than lastpagecount then it'd be a good
-  idea to increase max_fsm_pages.  Also, if the number of rows in
+  If storedpages is consistently less than interestingpages then it'd be a
+  good idea to increase max_fsm_pages.  Also, if the number of rows in
   pg_freespacemap_relations is close to max_fsm_relations, then you should
   consider increasing max_fsm_relations.
 
 
 regression=# \d pg_freespacemap_relations
 View "public.pg_freespacemap_relations"
-    Column     |  Type   | Modifiers
----------------+---------+-----------
- reltablespace | oid     |
- reldatabase   | oid     |
- relfilenode   | oid     |
- avgrequest    | integer |
- lastpagecount | integer |
- storedpages   | integer |
- nextpage      | integer |
+    Column        |  Type   | Modifiers
+------------------+---------+-----------
+ reltablespace    | oid     |
+ reldatabase      | oid     |
+ relfilenode      | oid     |
+ avgrequest       | integer |
+ interestingpages | integer |
+ storedpages      | integer |
+ nextpage         | integer |
 View definition:
- SELECT p.reltablespace, p.reldatabase, p.relfilenode, p.avgrequest, p.lastpagecount, p.storedpages, p.nextpage
-   FROM pg_freespacemap_relations() p(reltablespace oid, reldatabase oid, relfilenode oid, avgrequest integer, lastpagecount integer, storedpages integer, nextpage integer);
+ SELECT p.reltablespace, p.reldatabase, p.relfilenode, p.avgrequest, p.interestingpages, p.storedpages, p.nextpage
+   FROM pg_freespacemap_relations() p(reltablespace oid, reldatabase oid, relfilenode oid, avgrequest integer, interestingpages integer, storedpages integer, nextpage integer);
 
-regression=# SELECT c.relname, r.avgrequest, r.lastpagecount, r.storedpages
+regression=# SELECT c.relname, r.avgrequest, r.interestingpages, r.storedpages
              FROM pg_freespacemap_relations r INNER JOIN pg_class c
              ON c.relfilenode = r.relfilenode INNER JOIN pg_database d
              ON r.reldatabase = d.oid AND (d.datname = current_database()) 
              ORDER BY r.storedpages DESC LIMIT 10;
-             relname             | avgrequest | lastpagecount | storedpages
----------------------------------+------------+---------------+-------------
- onek                            |        256 |           109 |         109
- pg_attribute                    |        167 |            93 |          93
- pg_class                        |        191 |            49 |          49
- pg_attribute_relid_attnam_index |            |            48 |          48
- onek2                           |        256 |            37 |          37
- pg_depend                       |         95 |            26 |          26
- pg_type                         |        199 |            16 |          16
- pg_rewrite                      |       1011 |            13 |          13
- pg_class_relname_nsp_index      |            |            10 |          10
- pg_proc                         |        302 |             8 |           8
+             relname             | avgrequest | interestingpages | storedpages
+---------------------------------+------------+------------------+-------------
+ onek                            |        256 |              109 |         109
+ pg_attribute                    |        167 |               93 |          93
+ pg_class                        |        191 |               49 |          49
+ pg_attribute_relid_attnam_index |            |               48 |          48
+ onek2                           |        256 |               37 |          37
+ pg_depend                       |         95 |               26 |          26
+ pg_type                         |        199 |               16 |          16
+ pg_rewrite                      |       1011 |               13 |          13
+ pg_class_relname_nsp_index      |            |               10 |          10
+ pg_proc                         |        302 |                8 |           8
 (10 rows)
 
 
 
  * pg_freespacemap.c
  *   display some contents of the free space relation and page maps.
  *
- *   $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.6 2006/05/30 22:12:13 tgl Exp $
+ *   $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.7 2006/09/21 20:31:21 tgl Exp $
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
    Oid             reldatabase;
    Oid             relfilenode;
    Size            avgrequest;
-   int             lastpagecount;
+   BlockNumber     interestingpages;
    int             storedpages;
    int             nextpage;
    bool            isindex;
                           OIDOID, -1, 0);
        TupleDescInitEntry(tupledesc, (AttrNumber) 4, "avgrequest",
                           INT4OID, -1, 0);
-       TupleDescInitEntry(tupledesc, (AttrNumber) 5, "lastpagecount",
+       TupleDescInitEntry(tupledesc, (AttrNumber) 5, "interestingpages",
                           INT4OID, -1, 0);
        TupleDescInitEntry(tupledesc, (AttrNumber) 6, "storedpages",
                           INT4OID, -1, 0);
            fctx->record[i].reldatabase = fsmrel->key.dbNode;
            fctx->record[i].relfilenode = fsmrel->key.relNode;
            fctx->record[i].avgrequest = (int64)fsmrel->avgRequest;
-           fctx->record[i].lastpagecount = fsmrel->lastPageCount;
+           fctx->record[i].interestingpages = fsmrel->interestingPages;
            fctx->record[i].storedpages = fsmrel->storedPages;
            fctx->record[i].nextpage = fsmrel->nextPage;
            fctx->record[i].isindex = fsmrel->isIndex;
            values[3] = UInt32GetDatum(record->avgrequest);
            nulls[3] = false;
        }
-       values[4] = Int32GetDatum(record->lastpagecount);
+       values[4] = Int32GetDatum(record->interestingpages);
        nulls[4] = false;
        values[5] = Int32GetDatum(record->storedpages);
        nulls[5] = false;
 
     reldatabase oid,
     relfilenode oid,
     avgrequest integer,
-    lastpagecount integer,
+    interestingpages integer,
     storedpages integer,
     nextpage integer);
 
 
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *          $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.5 2006/07/31 20:08:59 tgl Exp $
+ *          $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.6 2006/09/21 20:31:21 tgl Exp $
  *-------------------------------------------------------------------------
  */
 
    bool     needLock;
     BlockNumber npages,
                blkno;
-   BlockNumber nFreePages,
+   BlockNumber totFreePages,
+               nFreePages,
               *freePages,
               maxFreePages;
    BlockNumber lastBlock = GIN_ROOT_BLKNO,
    if (maxFreePages > MaxFSMPages)
        maxFreePages = MaxFSMPages;
 
-   nFreePages = 0;
+   totFreePages = nFreePages = 0;
    freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
 
    for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++) {
        if ( GinPageIsDeleted(page) ) {
            if (nFreePages < maxFreePages)
                freePages[nFreePages++] = blkno;
+           totFreePages++;
        } else
            lastFilledBlock = blkno;
 
        int         i;
        for (i = 0; i < nFreePages; i++) 
            if (freePages[i] >= lastFilledBlock) {
-               nFreePages = i;
+               totFreePages = nFreePages = i;
                break;
            }
 
        stats->pages_removed = lastBlock - lastFilledBlock;
    }
 
-   RecordIndexFreeSpace(&index->rd_node, nFreePages, freePages);
-   stats->pages_free = nFreePages;
+   RecordIndexFreeSpace(&index->rd_node, totFreePages, nFreePages, freePages);
+   stats->pages_free = totFreePages;
 
    if (needLock)
        LockRelationForExtension(index, ExclusiveLock);
 
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.26 2006/07/31 20:08:59 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.27 2006/09/21 20:31:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    Relation    rel = info->index;
    BlockNumber npages,
                blkno;
-   BlockNumber nFreePages,
+   BlockNumber totFreePages,
+               nFreePages,
               *freePages,
                maxFreePages;
    BlockNumber lastBlock = GIST_ROOT_BLKNO,
    if (maxFreePages > MaxFSMPages)
        maxFreePages = MaxFSMPages;
 
-   nFreePages = 0;
+   totFreePages = nFreePages = 0;
    freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
+
    for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
    {
        Buffer      buffer;
        if (PageIsNew(page) || GistPageIsDeleted(page))
        {
            if (nFreePages < maxFreePages)
-           {
-               freePages[nFreePages] = blkno;
-               nFreePages++;
-           }
+               freePages[nFreePages++] = blkno;
+           totFreePages++;
        }
        else
            lastFilledBlock = blkno;
        for (i = 0; i < nFreePages; i++)
            if (freePages[i] >= lastFilledBlock)
            {
-               nFreePages = i;
+               totFreePages = nFreePages = i;
                break;
            }
 
        stats->std.pages_removed = lastBlock - lastFilledBlock;
    }
 
-   RecordIndexFreeSpace(&rel->rd_node, nFreePages, freePages);
+   RecordIndexFreeSpace(&rel->rd_node, totFreePages, nFreePages, freePages);
    pfree(freePages);
 
    /* return statistics */
-   stats->std.pages_free = nFreePages;
+   stats->std.pages_free = totFreePages;
    if (needLock)
        LockRelationForExtension(rel, ExclusiveLock);
    stats->std.num_pages = RelationGetNumberOfBlocks(rel);
 
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.150 2006/08/24 01:18:34 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.151 2006/09/21 20:31:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    void       *callback_state;
    BTCycleId   cycleid;
    BlockNumber *freePages;
-   int         nFreePages;
-   int         maxFreePages;
+   int         nFreePages;     /* number of entries in freePages[] */
+   int         maxFreePages;   /* allocated size of freePages[] */
+   BlockNumber totFreePages;   /* true total # of free pages */
    MemoryContext pagedelcontext;
 } BTVacState;
 
    vstate.freePages = NULL;    /* temporarily */
    vstate.nFreePages = 0;
    vstate.maxFreePages = 0;
+   vstate.totFreePages = 0;
 
    /* Create a temporary memory context to run _bt_pagedel in */
    vstate.pagedelcontext = AllocSetContextCreate(CurrentMemoryContext,
            new_pages--;
            stats->pages_deleted--;
            vstate.nFreePages--;
+           vstate.totFreePages = vstate.nFreePages;    /* can't be more */
        }
        if (new_pages != num_pages)
        {
     * pages in the index, discarding any old info the map may have. We do not
     * need to sort the page numbers; they're in order already.
     */
-   RecordIndexFreeSpace(&rel->rd_node, vstate.nFreePages, vstate.freePages);
+   RecordIndexFreeSpace(&rel->rd_node, vstate.totFreePages,
+                        vstate.nFreePages, vstate.freePages);
 
    pfree(vstate.freePages);
 
 
    /* update statistics */
    stats->num_pages = num_pages;
-   stats->pages_free = vstate.nFreePages;
+   stats->pages_free = vstate.totFreePages;
 }
 
 /*
        /* Okay to recycle this page */
        if (vstate->nFreePages < vstate->maxFreePages)
            vstate->freePages[vstate->nFreePages++] = blkno;
+       vstate->totFreePages++;
        stats->pages_deleted++;
    }
    else if (P_ISDELETED(opaque))
        {
            if (vstate->nFreePages < vstate->maxFreePages)
                vstate->freePages[vstate->nFreePages++] = blkno;
+           vstate->totFreePages++;
        }
 
        MemoryContextSwitchTo(oldcontext);
 
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.339 2006/09/17 22:16:22 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.340 2006/09/21 20:31:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
        }
    }
 
-   RecordRelationFreeSpace(&onerel->rd_node, outPages, pageSpaces);
+   RecordRelationFreeSpace(&onerel->rd_node, outPages, outPages, pageSpaces);
 
    pfree(pageSpaces);
 }
 
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.78 2006/09/13 17:47:08 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.79 2006/09/21 20:31:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    int         num_free_pages; /* current # of entries */
    int         max_free_pages; /* # slots allocated in array */
    PageFreeSpaceInfo *free_pages;      /* array or heap of blkno/avail */
+   BlockNumber tot_free_pages; /* total pages with >= threshold space */
 } LVRelStats;
 
 
                    tups_vacuumed, num_tuples, nblocks),
             errdetail("%.0f dead row versions cannot be removed yet.\n"
                       "There were %.0f unused item pointers.\n"
+                      "%u pages contain useful free space.\n"
                       "%u pages are entirely empty.\n"
                       "%s.",
                       nkeep,
                       nunused,
+                      vacrelstats->tot_free_pages,
                       empty_pages,
                       pg_rusage_show(&ru0))));
+
+   if (vacrelstats->tot_free_pages > MaxFSMPages)
+       ereport(WARNING,
+               (errmsg("relation \"%s.%s\" contains more than \"max_fsm_pages\" pages with useful free space",
+                       get_namespace_name(RelationGetNamespace(onerel)),
+                       relname),
+                errhint("Consider compacting this relation or increasing the configuration parameter \"max_fsm_pages\".")));
 }
 
 
        }
    }
    vacrelstats->num_free_pages = j;
+   /*
+    * If tot_free_pages was more than num_free_pages, we can't tell for sure
+    * what its correct value is now, because we don't know which of the
+    * forgotten pages are getting truncated.  Conservatively set it equal
+    * to num_free_pages.
+    */
+   vacrelstats->tot_free_pages = j;
+
    /* We destroyed the heap ordering, so mark array unordered */
    vacrelstats->fs_is_heap = false;
 
    vacrelstats->max_free_pages = maxpages;
    vacrelstats->free_pages = (PageFreeSpaceInfo *)
        palloc(maxpages * sizeof(PageFreeSpaceInfo));
+   vacrelstats->tot_free_pages = 0;
 }
 
 /*
    if (avail < vacrelstats->threshold)
        return;
 
+   /* Count all pages over threshold, even if not enough space in array */
+   vacrelstats->tot_free_pages++;
+
    /* Copy pointers to local variables for notational simplicity */
    pageSpaces = vacrelstats->free_pages;
    n = vacrelstats->max_free_pages;
        qsort(pageSpaces, nPages, sizeof(PageFreeSpaceInfo),
              vac_cmp_page_spaces);
 
-   RecordRelationFreeSpace(&onerel->rd_node, nPages, pageSpaces);
+   RecordRelationFreeSpace(&onerel->rd_node, vacrelstats->tot_free_pages,
+                           nPages, pageSpaces);
 }
 
 /*
 
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.54 2006/07/14 14:52:22 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.55 2006/09/21 20:31:22 tgl Exp $
  *
  *
  * NOTES:
  * behavior keeps track of which relation is least recently used.
  *
  * For each known relation, we track the average request size given to
- * GetPageWithFreeSpace() as well as the most recent number of pages given
+ * GetPageWithFreeSpace() as well as the most recent number of pages reported
  * to RecordRelationFreeSpace().  The average request size is not directly
  * used in this module, but we expect VACUUM to use it to filter out
  * uninteresting amounts of space before calling RecordRelationFreeSpace().
  *     relfilenode
  *     isIndex
  *     avgRequest
- *     lastPageCount
+ *     interestingPages
  *     storedPages
  *     arena data      array of storedPages FSMPageData or IndexFSMPageData
  *----------
    RelFileNode key;            /* hash key (must be first) */
    bool        isIndex;        /* if true, we store only page numbers */
    uint32      avgRequest;     /* moving average of space requests */
-   int32       lastPageCount;  /* pages passed to RecordRelationFreeSpace */
+   BlockNumber interestingPages;   /* # of pages with useful free space */
    int32       storedPages;    /* # of pages stored in arena */
 } FsmCacheRelHeader;
 
 static FSMRelation *lookup_fsm_rel(RelFileNode *rel);
 static FSMRelation *create_fsm_rel(RelFileNode *rel);
 static void delete_fsm_rel(FSMRelation *fsmrel);
-static int realloc_fsm_rel(FSMRelation *fsmrel, int nPages, bool isIndex);
+static int realloc_fsm_rel(FSMRelation *fsmrel, BlockNumber interestingPages,
+                           bool isIndex);
 static void link_fsm_rel_usage(FSMRelation *fsmrel);
 static void unlink_fsm_rel_usage(FSMRelation *fsmrel);
 static void link_fsm_rel_storage(FSMRelation *fsmrel);
 static void pack_existing_pages(FSMPageData *newLocation, int newPages,
                    FSMPageData *oldLocation, int oldPages);
 static int fsm_calc_request(FSMRelation *fsmrel);
+static int fsm_calc_request_unclamped(FSMRelation *fsmrel);
 static int fsm_calc_target_allocation(int myRequest);
 static int fsm_current_chunks(FSMRelation *fsmrel);
 static int fsm_current_allocation(FSMRelation *fsmrel);
  *
  * Any pre-existing info about the relation is assumed obsolete and discarded.
  *
+ * interestingPages is the total number of pages in the relation that have
+ * at least threshold free space; nPages is the number actually reported in
+ * pageSpaces[] (may be less --- in particular, callers typically clamp their
+ * space usage to MaxFSMPages).
+ *
  * The given pageSpaces[] array must be sorted in order by blkno.  Note that
  * the FSM is at liberty to discard some or all of the data.
  */
 void
 RecordRelationFreeSpace(RelFileNode *rel,
+                       BlockNumber interestingPages,
                        int nPages,
                        PageFreeSpaceInfo *pageSpaces)
 {
        int         curAllocPages;
        FSMPageData *newLocation;
 
-       curAlloc = realloc_fsm_rel(fsmrel, nPages, false);
+       curAlloc = realloc_fsm_rel(fsmrel, interestingPages, false);
        curAllocPages = curAlloc * CHUNKPAGES;
 
        /*
  */
 void
 RecordIndexFreeSpace(RelFileNode *rel,
+                    BlockNumber interestingPages,
                     int nPages,
                     BlockNumber *pages)
 {
        int         i;
        IndexFSMPageData *newLocation;
 
-       curAlloc = realloc_fsm_rel(fsmrel, nPages, true);
+       curAlloc = realloc_fsm_rel(fsmrel, interestingPages, true);
        curAllocPages = curAlloc * INDEXCHUNKPAGES;
 
        /*
        (void) lookup_fsm_page_entry(fsmrel, nblocks, &pageIndex);
        /* Delete all such entries */
        fsmrel->storedPages = pageIndex;
-       /* XXX should we adjust rel's lastPageCount and sumRequests? */
+       /* XXX should we adjust rel's interestingPages and sumRequests? */
    }
    LWLockRelease(FreeSpaceLock);
 }
 {
    FSMRelation *fsmrel;
    int         storedPages = 0;
+   double      sumRequests = 0;
    int         numRels;
-   double      sumRequests;
    double      needed;
 
    LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE);
-   /* Count total space used --- tedious, but seems useful */
+   /*
+    * Count total space actually used, as well as the unclamped request total
+    */
    for (fsmrel = FreeSpaceMap->firstRel;
         fsmrel != NULL;
         fsmrel = fsmrel->nextPhysical)
+   {
        storedPages += fsmrel->storedPages;
+       sumRequests += fsm_calc_request_unclamped(fsmrel);
+   }
 
    /* Copy other stats before dropping lock */
    numRels = FreeSpaceMap->numRels;
-   sumRequests = FreeSpaceMap->sumRequests;
    LWLockRelease(FreeSpaceLock);
 
    /* Convert stats to actual number of page slots needed */
              "%.0f page slots are required to track all free space.\n"
          "Current limits are:  %d page slots, %d relations, using %.0f KB.",
              Min(needed, MaxFSMPages),
-             needed, MaxFSMPages, MaxFSMRelations,
+             needed,
+             MaxFSMPages, MaxFSMRelations,
              (double) FreeSpaceShmemSize() / 1024.0)));
 
    CheckFreeSpaceMapStatistics(NOTICE, numRels, needed);
        relheader.key = fsmrel->key;
        relheader.isIndex = fsmrel->isIndex;
        relheader.avgRequest = fsmrel->avgRequest;
-       relheader.lastPageCount = fsmrel->lastPageCount;
+       relheader.interestingPages = fsmrel->interestingPages;
        relheader.storedPages = fsmrel->storedPages;
        if (fwrite(&relheader, 1, sizeof(relheader), fp) != sizeof(relheader))
            goto write_failed;
        if (fread(&relheader, 1, sizeof(relheader), fp) != sizeof(relheader) ||
            (relheader.isIndex != false && relheader.isIndex != true) ||
            relheader.avgRequest >= BLCKSZ ||
-           relheader.lastPageCount < 0 ||
            relheader.storedPages < 0)
        {
            elog(LOG, "bogus rel header in \"%s\"", FSM_CACHE_FILENAME);
            goto read_failed;
        }
 
-       /* Make sure lastPageCount doesn't exceed current MaxFSMPages */
-       if (relheader.lastPageCount > MaxFSMPages)
-           relheader.lastPageCount = MaxFSMPages;
-
        /* Read the per-page data */
        nPages = relheader.storedPages;
        if (relheader.isIndex)
        fsmrel = create_fsm_rel(&relheader.key);
        fsmrel->avgRequest = relheader.avgRequest;
 
-       curAlloc = realloc_fsm_rel(fsmrel, relheader.lastPageCount,
+       curAlloc = realloc_fsm_rel(fsmrel, relheader.interestingPages,
                                   relheader.isIndex);
        if (relheader.isIndex)
        {
        /* New hashtable entry, initialize it (hash_search set the key) */
        fsmrel->isIndex = false;    /* until we learn different */
        fsmrel->avgRequest = INITIAL_AVERAGE;
-       fsmrel->lastPageCount = 0;
+       fsmrel->interestingPages = 0;
        fsmrel->firstChunk = -1;    /* no space allocated */
        fsmrel->storedPages = 0;
        fsmrel->nextPage = 0;
  * The return value is the actual new allocation, in chunks.
  */
 static int
-realloc_fsm_rel(FSMRelation *fsmrel, int nPages, bool isIndex)
+realloc_fsm_rel(FSMRelation *fsmrel, BlockNumber interestingPages,
+               bool isIndex)
 {
    int         myRequest;
    int         myAlloc;
     */
    fsmrel->storedPages = 0;
    FreeSpaceMap->sumRequests -= fsm_calc_request(fsmrel);
-   fsmrel->lastPageCount = nPages;
+   fsmrel->interestingPages = interestingPages;
    fsmrel->isIndex = isIndex;
    myRequest = fsm_calc_request(fsmrel);
    FreeSpaceMap->sumRequests += myRequest;
     * new data in-place.
     */
    curAlloc = fsm_current_allocation(fsmrel);
-   if (myAlloc > curAlloc && (myRequest + 1) > curAlloc && nPages > 0)
+   if (myAlloc > curAlloc && (myRequest + 1) > curAlloc && interestingPages > 0)
    {
        /* Remove entry from storage list, and compact */
        unlink_fsm_rel_storage(fsmrel);
 }
 
 /*
- * Calculate number of chunks "requested" by a rel.
+ * Calculate number of chunks "requested" by a rel.  The "request" is
+ * anything beyond the rel's one guaranteed chunk.
  *
- * Rel's lastPageCount and isIndex settings must be up-to-date when called.
+ * Rel's interestingPages and isIndex settings must be up-to-date when called.
  *
  * See notes at top of file for details.
  */
 static int
 fsm_calc_request(FSMRelation *fsmrel)
 {
-   int         chunkCount;
+   int         req;
 
    /* Convert page count to chunk count */
    if (fsmrel->isIndex)
-       chunkCount = (fsmrel->lastPageCount - 1) / INDEXCHUNKPAGES + 1;
+   {
+       /* test to avoid unsigned underflow at zero */
+       if (fsmrel->interestingPages <= INDEXCHUNKPAGES)
+           return 0;
+       /* quotient will fit in int, even if interestingPages doesn't */
+       req = (fsmrel->interestingPages - 1) / INDEXCHUNKPAGES;
+   }
    else
-       chunkCount = (fsmrel->lastPageCount - 1) / CHUNKPAGES + 1;
-   /* "Request" is anything beyond our one guaranteed chunk */
-   if (chunkCount <= 0)
-       return 0;
+   {
+       if (fsmrel->interestingPages <= CHUNKPAGES)
+           return 0;
+       req = (fsmrel->interestingPages - 1) / CHUNKPAGES;
+   }
+
+   /*
+    * We clamp the per-relation requests to at most half the arena size;
+    * this is intended to prevent a single bloated relation from crowding
+    * out FSM service for every other rel.
+    */
+   req = Min(req, FreeSpaceMap->totalChunks / 2);
+
+   return req;
+}
+
+/*
+ * Same as above, but without the clamp ... this is just intended for
+ * reporting the total space needed to store all information.
+ */
+static int
+fsm_calc_request_unclamped(FSMRelation *fsmrel)
+{
+   int         req;
+
+   /* Convert page count to chunk count */
+   if (fsmrel->isIndex)
+   {
+       /* test to avoid unsigned underflow at zero */
+       if (fsmrel->interestingPages <= INDEXCHUNKPAGES)
+           return 0;
+       /* quotient will fit in int, even if interestingPages doesn't */
+       req = (fsmrel->interestingPages - 1) / INDEXCHUNKPAGES;
+   }
    else
-       return chunkCount - 1;
+   {
+       if (fsmrel->interestingPages <= CHUNKPAGES)
+           return 0;
+       req = (fsmrel->interestingPages - 1) / CHUNKPAGES;
+   }
+
+   return req;
 }
 
 /*
    for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = fsmrel->nextUsage)
    {
        relNum++;
-       fprintf(stderr, "Map %d: rel %u/%u/%u isIndex %d avgRequest %u lastPageCount %d nextPage %d\nMap= ",
+       fprintf(stderr, "Map %d: rel %u/%u/%u isIndex %d avgRequest %u interestingPages %u nextPage %d\nMap= ",
                relNum,
                fsmrel->key.spcNode, fsmrel->key.dbNode, fsmrel->key.relNode,
                (int) fsmrel->isIndex, fsmrel->avgRequest,
-               fsmrel->lastPageCount, fsmrel->nextPage);
+               fsmrel->interestingPages, fsmrel->nextPage);
        if (fsmrel->isIndex)
        {
            IndexFSMPageData *page;
 
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/freespace.h,v 1.21 2006/07/13 16:49:20 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/freespace.h,v 1.22 2006/09/21 20:31:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    FSMRelation *priorPhysical; /* prior rel in arena-storage order */
    bool        isIndex;        /* if true, we store only page numbers */
    Size        avgRequest;     /* moving average of space requests */
-   int         lastPageCount;  /* pages passed to RecordRelationFreeSpace */
+   BlockNumber interestingPages;   /* # of pages with useful free space */
    int         firstChunk;     /* chunk # of my first chunk in arena */
    int         storedPages;    /* # of pages stored in arena */
    int         nextPage;       /* index (from 0) to start next search at */
  */
 extern void InitFreeSpaceMap(void);
 extern Size FreeSpaceShmemSize(void);
+extern FSMHeader *GetFreeSpaceMap(void);
 
 extern BlockNumber GetPageWithFreeSpace(RelFileNode *rel, Size spaceNeeded);
 extern BlockNumber RecordAndGetPageWithFreeSpace(RelFileNode *rel,
                              Size spaceNeeded);
 extern Size GetAvgFSMRequestSize(RelFileNode *rel);
 extern void RecordRelationFreeSpace(RelFileNode *rel,
-                       int nPages,
-                       PageFreeSpaceInfo *pageSpaces);
+                                   BlockNumber interestingPages,
+                                   int nPages,
+                                   PageFreeSpaceInfo *pageSpaces);
 
 extern BlockNumber GetFreeIndexPage(RelFileNode *rel);
 extern void RecordIndexFreeSpace(RelFileNode *rel,
-                    int nPages,
-                    BlockNumber *pages);
+                                BlockNumber interestingPages,
+                                int nPages,
+                                BlockNumber *pages);
 
 extern void FreeSpaceMapTruncateRel(RelFileNode *rel, BlockNumber nblocks);
 extern void FreeSpaceMapForgetRel(RelFileNode *rel);
 
 extern void DumpFreeSpaceMap(int code, Datum arg);
 extern void LoadFreeSpaceMap(void);
-extern FSMHeader *GetFreeSpaceMap(void);
 
 #ifdef FREESPACE_DEBUG
 extern void DumpFreeSpace(void);