1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-31 22:04:40 +03:00

Rewrite the FSM. Instead of relying on a fixed-size shared memory segment, the

free space information is stored in a dedicated FSM relation fork, with each
relation (except for hash indexes; they don't use FSM).

This eliminates the max_fsm_relations and max_fsm_pages GUC options; remove any
trace of them from the backend, initdb, and documentation.

Rewrite contrib/pg_freespacemap to match the new FSM implementation. Also
introduce a new variant of the get_raw_page(regclass, int4, int4) function in
contrib/pageinspect that let's you to return pages from any relation fork, and
a new fsm_page_contents() function to inspect the new FSM pages.
This commit is contained in:
Heikki Linnakangas
2008-09-30 10:52:14 +00:00
parent 2dbc0ca937
commit 15c121b3ed
53 changed files with 1844 additions and 2720 deletions

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.14 2008/07/11 21:06:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.15 2008/09/30 10:52:10 heikki Exp $
*-------------------------------------------------------------------------
*/
@ -19,6 +19,7 @@
#include "catalog/index.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "utils/memutils.h"
@ -283,6 +284,9 @@ ginbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index));
/* Initialize FSM */
InitIndexFreeSpaceMap(index);
initGinState(&buildstate.ginstate, index);
/* initialize the root page */

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.16 2008/07/11 21:06:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.17 2008/09/30 10:52:10 heikki Exp $
*-------------------------------------------------------------------------
*/
@ -19,6 +19,7 @@
#include "catalog/pg_type.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
void
@ -151,7 +152,7 @@ GinNewBuffer(Relation index)
/* First, try to get a page from FSM */
for (;;)
{
BlockNumber blkno = GetFreeIndexPage(&index->rd_node);
BlockNumber blkno = GetFreeIndexPage(index);
if (blkno == InvalidBlockNumber)
break;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.21 2008/07/11 21:06:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.22 2008/09/30 10:52:10 heikki Exp $
*-------------------------------------------------------------------------
*/
@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
typedef struct
@ -678,10 +679,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
bool needLock;
BlockNumber npages,
blkno;
BlockNumber totFreePages,
nFreePages,
*freePages,
maxFreePages;
BlockNumber totFreePages;
BlockNumber lastBlock = GIN_ROOT_BLKNO,
lastFilledBlock = GIN_ROOT_BLKNO;
@ -711,12 +709,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
if (needLock)
UnlockRelationForExtension(index, ExclusiveLock);
maxFreePages = npages;
if (maxFreePages > MaxFSMPages)
maxFreePages = MaxFSMPages;
totFreePages = nFreePages = 0;
freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
totFreePages = 0;
for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++)
{
@ -731,8 +724,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
if (GinPageIsDeleted(page))
{
if (nFreePages < maxFreePages)
freePages[nFreePages++] = blkno;
RecordFreeIndexPage(index, blkno);
totFreePages++;
}
else
@ -742,25 +734,16 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
}
lastBlock = npages - 1;
if (info->vacuum_full && nFreePages > 0)
if (info->vacuum_full && lastBlock > lastFilledBlock)
{
/* try to truncate index */
int i;
for (i = 0; i < nFreePages; i++)
if (freePages[i] >= lastFilledBlock)
{
totFreePages = nFreePages = i;
break;
}
if (lastBlock > lastFilledBlock)
RelationTruncate(index, lastFilledBlock + 1);
FreeSpaceMapTruncateRel(index, lastFilledBlock + 1);
RelationTruncate(index, lastFilledBlock + 1);
stats->pages_removed = lastBlock - lastFilledBlock;
totFreePages = totFreePages - stats->pages_removed;
}
RecordIndexFreeSpace(&index->rd_node, totFreePages, nFreePages, freePages);
stats->pages_free = totFreePages;
if (needLock)

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.151 2008/06/12 09:12:29 heikki Exp $
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.152 2008/09/30 10:52:10 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,6 +19,7 @@
#include "catalog/index.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "utils/memutils.h"
const XLogRecPtr XLogRecPtrForTemp = {1, 1};
@ -102,6 +103,9 @@ gistbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index));
/* Initialize FSM */
InitIndexFreeSpaceMap(index);
/* no locking is needed */
initGISTstate(&buildstate.giststate, index);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.30 2008/07/13 20:45:46 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.31 2008/09/30 10:52:10 heikki Exp $
*-------------------------------------------------------------------------
*/
#include "postgres.h"
@ -16,6 +16,7 @@
#include "access/gist_private.h"
#include "access/reloptions.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "storage/bufmgr.h"
#include "utils/rel.h"
@ -617,7 +618,7 @@ gistNewBuffer(Relation r)
/* First, try to get a page from FSM */
for (;;)
{
BlockNumber blkno = GetFreeIndexPage(&r->rd_node);
BlockNumber blkno = GetFreeIndexPage(r);
if (blkno == InvalidBlockNumber)
break; /* nothing left in FSM */

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.36 2008/06/12 09:12:30 heikki Exp $
* $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.37 2008/09/30 10:52:10 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/memutils.h"
@ -518,10 +519,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
Relation rel = info->index;
BlockNumber npages,
blkno;
BlockNumber totFreePages,
nFreePages,
*freePages,
maxFreePages;
BlockNumber totFreePages;
BlockNumber lastBlock = GIST_ROOT_BLKNO,
lastFilledBlock = GIST_ROOT_BLKNO;
bool needLock;
@ -589,13 +587,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
if (needLock)
UnlockRelationForExtension(rel, ExclusiveLock);
maxFreePages = npages;
if (maxFreePages > MaxFSMPages)
maxFreePages = MaxFSMPages;
totFreePages = nFreePages = 0;
freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
totFreePages = 0;
for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
{
Buffer buffer;
@ -609,9 +601,8 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
if (PageIsNew(page) || GistPageIsDeleted(page))
{
if (nFreePages < maxFreePages)
freePages[nFreePages++] = blkno;
totFreePages++;
RecordFreeIndexPage(rel, blkno);
}
else
lastFilledBlock = blkno;
@ -619,25 +610,15 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
}
lastBlock = npages - 1;
if (info->vacuum_full && nFreePages > 0)
if (info->vacuum_full && lastFilledBlock < lastBlock)
{ /* try to truncate index */
int i;
FreeSpaceMapTruncateRel(rel, lastFilledBlock + 1);
RelationTruncate(rel, lastFilledBlock + 1);
for (i = 0; i < nFreePages; i++)
if (freePages[i] >= lastFilledBlock)
{
totFreePages = nFreePages = i;
break;
}
if (lastBlock > lastFilledBlock)
RelationTruncate(rel, lastFilledBlock + 1);
stats->std.pages_removed = lastBlock - lastFilledBlock;
totFreePages = totFreePages - stats->std.pages_removed;
}
RecordIndexFreeSpace(&rel->rd_node, totFreePages, nFreePages, freePages);
pfree(freePages);
/* return statistics */
stats->std.pages_free = totFreePages;
if (needLock)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.263 2008/09/11 14:01:09 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.264 2008/09/30 10:52:10 heikki Exp $
*
*
* INTERFACE ROUTINES
@ -4721,6 +4721,9 @@ heap_sync(Relation rel)
/* FlushRelationBuffers will have opened rd_smgr */
smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
/* sync FSM as well */
smgrimmedsync(rel->rd_smgr, FSM_FORKNUM);
/* toast heap, if any */
if (OidIsValid(rel->rd_rel->reltoastrelid))
{
@ -4729,6 +4732,7 @@ heap_sync(Relation rel)
toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
FlushRelationBuffers(toastrel);
smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
smgrimmedsync(toastrel->rd_smgr, FSM_FORKNUM);
heap_close(toastrel, AccessShareLock);
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.72 2008/07/13 20:45:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.73 2008/09/30 10:52:10 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -163,8 +163,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
* We have no cached target page, so ask the FSM for an initial
* target.
*/
targetBlock = GetPageWithFreeSpace(&relation->rd_node,
len + saveFreeSpace);
targetBlock = GetPageWithFreeSpace(relation, len + saveFreeSpace);
/*
* If the FSM knows nothing of the rel, try the last page before we
@ -250,7 +249,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
* Update FSM as to condition of this page, and ask for another page
* to try.
*/
targetBlock = RecordAndGetPageWithFreeSpace(&relation->rd_node,
targetBlock = RecordAndGetPageWithFreeSpace(relation,
targetBlock,
pageFreeSpace,
len + saveFreeSpace);

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.110 2008/07/13 20:45:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.111 2008/09/30 10:52:10 heikki Exp $
*
* NOTES
* Postgres btree pages look like ordinary relation pages. The opaque
@ -27,6 +27,7 @@
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/inval.h"
#include "utils/snapmgr.h"
@ -501,7 +502,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
*/
for (;;)
{
blkno = GetFreeIndexPage(&rel->rd_node);
blkno = GetFreeIndexPage(rel);
if (blkno == InvalidBlockNumber)
break;
buf = ReadBuffer(rel, blkno);

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.161 2008/06/19 00:46:03 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.162 2008/09/30 10:52:10 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,6 +26,7 @@
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "utils/memutils.h"
@ -56,9 +57,7 @@ typedef struct
IndexBulkDeleteCallback callback;
void *callback_state;
BTCycleId cycleid;
BlockNumber *freePages;
int nFreePages; /* number of entries in freePages[] */
int maxFreePages; /* allocated size of freePages[] */
BlockNumber lastUsedPage;
BlockNumber totFreePages; /* true total # of free pages */
MemoryContext pagedelcontext;
} BTVacState;
@ -110,6 +109,9 @@ btbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index));
/* Initialize FSM */
InitIndexFreeSpaceMap(index);
buildstate.spool = _bt_spoolinit(index, indexInfo->ii_Unique, false);
/*
@ -623,9 +625,7 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
vstate.callback = callback;
vstate.callback_state = callback_state;
vstate.cycleid = cycleid;
vstate.freePages = NULL; /* temporarily */
vstate.nFreePages = 0;
vstate.maxFreePages = 0;
vstate.lastUsedPage = BTREE_METAPAGE;
vstate.totFreePages = 0;
/* Create a temporary memory context to run _bt_pagedel in */
@ -670,17 +670,6 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
if (needLock)
UnlockRelationForExtension(rel, ExclusiveLock);
/* Allocate freePages after we read num_pages the first time */
if (vstate.freePages == NULL)
{
/* No point in remembering more than MaxFSMPages pages */
vstate.maxFreePages = MaxFSMPages;
if ((BlockNumber) vstate.maxFreePages > num_pages)
vstate.maxFreePages = (int) num_pages;
vstate.freePages = (BlockNumber *)
palloc(vstate.maxFreePages * sizeof(BlockNumber));
}
/* Quit if we've scanned the whole relation */
if (blkno >= num_pages)
break;
@ -697,42 +686,22 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
* acquiring exclusive lock on the index and then rechecking all the
* pages; doesn't seem worth it.
*/
if (info->vacuum_full && vstate.nFreePages > 0)
if (info->vacuum_full && vstate.lastUsedPage < num_pages - 1)
{
BlockNumber new_pages = num_pages;
BlockNumber new_pages = vstate.lastUsedPage + 1;
while (vstate.nFreePages > 0 &&
vstate.freePages[vstate.nFreePages - 1] == new_pages - 1)
{
new_pages--;
stats->pages_deleted--;
vstate.nFreePages--;
vstate.totFreePages = vstate.nFreePages; /* can't be more */
}
if (new_pages != num_pages)
{
/*
* Okay to truncate.
*/
RelationTruncate(rel, new_pages);
/*
* Okay to truncate.
*/
FreeSpaceMapTruncateRel(rel, new_pages);
RelationTruncate(rel, new_pages);
/* update statistics */
stats->pages_removed += num_pages - new_pages;
num_pages = new_pages;
}
/* update statistics */
stats->pages_removed += num_pages - new_pages;
vstate.totFreePages -= (num_pages - new_pages);
num_pages = new_pages;
}
/*
* Update the shared Free Space Map with the info we now have about free
* 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.totFreePages,
vstate.nFreePages, vstate.freePages);
pfree(vstate.freePages);
MemoryContextDelete(vstate.pagedelcontext);
/* update statistics */
@ -788,8 +757,7 @@ restart:
/*
* If we are recursing, the only case we want to do anything with is a
* live leaf page having the current vacuum cycle ID. Any other state
* implies we already saw the page (eg, deleted it as being empty). In
* particular, we don't want to risk adding it to freePages twice.
* implies we already saw the page (eg, deleted it as being empty).
*/
if (blkno != orig_blkno)
{
@ -803,12 +771,15 @@ restart:
}
}
/* If the page is in use, update lastUsedPage */
if (!_bt_page_recyclable(page) && vstate->lastUsedPage < blkno)
vstate->lastUsedPage = blkno;
/* Page is valid, see what to do with it */
if (_bt_page_recyclable(page))
{
/* Okay to recycle this page */
if (vstate->nFreePages < vstate->maxFreePages)
vstate->freePages[vstate->nFreePages++] = blkno;
RecordFreeIndexPage(rel, blkno);
vstate->totFreePages++;
stats->pages_deleted++;
}
@ -944,8 +915,7 @@ restart:
*/
if (ndel && info->vacuum_full)
{
if (vstate->nFreePages < vstate->maxFreePages)
vstate->freePages[vstate->nFreePages++] = blkno;
RecordFreeIndexPage(rel, blkno);
vstate->totFreePages++;
}

View File

@ -52,12 +52,14 @@
* we log the completed index pages to WAL if and only if WAL archiving is
* active.
*
* This code isn't concerned about the FSM at all. The caller is responsible
* for initializing that.
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.117 2008/08/11 11:05:10 heikki Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.118 2008/09/30 10:52:10 heikki Exp $
*
*-------------------------------------------------------------------------
*/

View File

@ -3,7 +3,7 @@
*
* Resource managers definition
*
* $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.25 2006/11/05 22:42:07 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.26 2008/09/30 10:52:11 heikki Exp $
*/
#include "postgres.h"
@ -19,6 +19,7 @@
#include "commands/dbcommands.h"
#include "commands/sequence.h"
#include "commands/tablespace.h"
#include "storage/freespace.h"
#include "storage/smgr.h"
@ -30,7 +31,7 @@ const RmgrData RmgrTable[RM_MAX_ID + 1] = {
{"Database", dbase_redo, dbase_desc, NULL, NULL, NULL},
{"Tablespace", tblspc_redo, tblspc_desc, NULL, NULL, NULL},
{"MultiXact", multixact_redo, multixact_desc, NULL, NULL, NULL},
{"Reserved 7", NULL, NULL, NULL, NULL, NULL},
{"FreeSpaceMap", fsm_redo, fsm_desc, NULL, NULL, NULL},
{"Reserved 8", NULL, NULL, NULL, NULL, NULL},
{"Heap2", heap2_redo, heap2_desc, NULL, NULL, NULL},
{"Heap", heap_redo, heap_desc, NULL, NULL, NULL},

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.58 2008/08/11 11:05:10 heikki Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.59 2008/09/30 10:52:11 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -359,6 +359,7 @@ CreateFakeRelcacheEntry(RelFileNode rnode)
rel->rd_lockInfo.lockRelId.relId = rnode.relNode;
rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks_cache = InvalidBlockNumber;
rel->rd_smgr = NULL;
return rel;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.245 2008/09/01 20:42:43 tgl Exp $
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.246 2008/09/30 10:52:11 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,7 +33,6 @@
#include "postmaster/bgwriter.h"
#include "postmaster/walwriter.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
@ -419,7 +418,6 @@ AuxiliaryProcessMain(int argc, char *argv[])
case StartupProcess:
bootstrap_signals();
StartupXLOG();
LoadFreeSpaceMap();
BuildFlatFiles(false);
proc_exit(0); /* startup done */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.339 2008/08/28 23:09:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.340 2008/09/30 10:52:12 heikki Exp $
*
*
* INTERFACE ROUTINES
@ -56,6 +56,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@ -294,14 +295,22 @@ heap_create(const char *relname,
/*
* Have the storage manager create the relation's disk file, if needed.
*
* We only create storage for the main fork here. The caller is
* responsible for creating any additional forks if needed.
* We create storage for the main fork here, and also for the FSM for a
* heap or toast relation. The caller is responsible for creating any
* additional forks if needed.
*/
if (create_storage)
{
Assert(rel->rd_smgr == NULL);
RelationOpenSmgr(rel);
smgrcreate(rel->rd_smgr, MAIN_FORKNUM, rel->rd_istemp, false);
/*
* For a real heap, create FSM fork as well. Indexams are
* responsible for creating any extra forks themselves.
*/
if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE)
smgrcreate(rel->rd_smgr, FSM_FORKNUM, rel->rd_istemp, false);
}
return rel;
@ -2256,7 +2265,11 @@ RelationTruncateIndexes(Relation heapRelation)
/* Fetch info needed for index_build */
indexInfo = BuildIndexInfo(currentIndex);
/* Now truncate the actual file (and discard buffers) */
/*
* Now truncate the actual file (and discard buffers). The indexam
* is responsible for truncating the FSM in index_build(), if
* applicable.
*/
RelationTruncate(currentIndex, 0);
/* Initialize the index and rebuild */
@ -2310,7 +2323,8 @@ heap_truncate(List *relids)
{
Relation rel = lfirst(cell);
/* Truncate the actual file (and discard buffers) */
/* Truncate the FSM and actual file (and discard buffers) */
FreeSpaceMapTruncateRel(rel, 0);
RelationTruncate(rel, 0);
/* If this relation has indexes, truncate the indexes too */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.304 2008/09/15 18:43:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.305 2008/09/30 10:52:12 heikki Exp $
*
*
* INTERFACE ROUTINES
@ -920,7 +920,7 @@ index_drop(Oid indexId)
RelationOpenSmgr(userIndexRelation);
for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
if (smgrexists(userIndexRelation->rd_smgr, forknum))
smgrscheduleunlink(userIndexRelation->rd_smgr, forknum,
smgrscheduleunlink(userIndexRelation->rd_smgr, forknum,
userIndexRelation->rd_istemp);
RelationCloseSmgr(userIndexRelation);
@ -1322,7 +1322,7 @@ setNewRelfilenode(Relation relation, TransactionId freezeXid)
/*
* ... and create storage for corresponding forks in the new relfilenode.
*
* NOTE: any conflict in relfilenode value will be caught here
* NOTE: any conflict in relfilenode value will be caught here
*/
newrnode = relation->rd_node;
newrnode.relNode = newrelfilenode;
@ -1331,6 +1331,14 @@ setNewRelfilenode(Relation relation, TransactionId freezeXid)
/* Create the main fork, like heap_create() does */
smgrcreate(srel, MAIN_FORKNUM, relation->rd_istemp, false);
/*
* For a heap, create FSM fork as well. Indexams are responsible for
* creating any extra forks themselves.
*/
if (relation->rd_rel->relkind == RELKIND_RELATION ||
relation->rd_rel->relkind == RELKIND_TOASTVALUE)
smgrcreate(srel, FSM_FORKNUM, relation->rd_istemp, false);
/* schedule unlinking old files */
for (i = 0; i <= MAX_FORKNUM; i++)
{
@ -2310,7 +2318,10 @@ reindex_index(Oid indexId)
if (inplace)
{
/* Truncate the actual file (and discard buffers) */
/*
* Truncate the actual file (and discard buffers). The indexam
* is responsible for truncating the FSM, if applicable
*/
RelationTruncate(iRel, 0);
}
else

View File

@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.212 2008/09/23 10:58:03 heikki Exp $
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.213 2008/09/30 10:52:12 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,7 +43,6 @@
#include "postmaster/bgwriter.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
@ -796,11 +795,6 @@ dropdb(const char *dbname, bool missing_ok)
*/
DropDatabaseBuffers(db_id);
/*
* Also, clean out any entries in the shared free space map.
*/
FreeSpaceMapForgetDatabase(db_id);
/*
* Tell the stats collector to forget it immediately, too.
*/
@ -1640,9 +1634,6 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
/* Drop pages for this database that are in the shared buffer cache */
DropDatabaseBuffers(xlrec->db_id);
/* Also, clean out any entries in the shared free space map */
FreeSpaceMapForgetDatabase(xlrec->db_id);
/* Also, clean out any fsync requests that might be pending in md.c */
ForgetDatabaseFsyncRequests(xlrec->db_id);

View File

@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.377 2008/09/11 14:01:09 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.378 2008/09/30 10:52:12 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -505,14 +505,6 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
* (autovacuum.c does this for itself.)
*/
vac_update_datfrozenxid();
/*
* If it was a database-wide VACUUM, print FSM usage statistics (we
* don't make you be superuser to see these). We suppress this in
* autovacuum, too.
*/
if (all_rels)
PrintFreeSpaceMapStatistics(elevel);
}
/*
@ -1272,8 +1264,9 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
}
}
/* update shared free space map with final free space info */
/* update thefree space map with final free space info, and vacuum it */
vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages);
FreeSpaceMapVacuum(onerel);
/* update statistics in pg_class */
vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
@ -2849,6 +2842,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
/* Truncate relation, if needed */
if (blkno < nblocks)
{
FreeSpaceMapTruncateRel(onerel, blkno);
RelationTruncate(onerel, blkno);
vacrelstats->rel_pages = blkno; /* set new number of blocks */
}
@ -3243,6 +3237,7 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages)
(errmsg("\"%s\": truncated %u to %u pages",
RelationGetRelationName(onerel),
vacrelstats->rel_pages, relblocks)));
FreeSpaceMapTruncateRel(onerel, relblocks);
RelationTruncate(onerel, relblocks);
vacrelstats->rel_pages = relblocks; /* set new number of blocks */
}
@ -3475,8 +3470,8 @@ tid_reaped(ItemPointer itemptr, void *state)
}
/*
* Update the shared Free Space Map with the info we now have about
* free space in the relation, discarding any old info the map may have.
* Update the Free Space Map with the info we now have about free space in
* the relation.
*/
static void
vac_update_fsm(Relation onerel, VacPageList fraged_pages,
@ -3484,26 +3479,8 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages,
{
int nPages = fraged_pages->num_pages;
VacPage *pagedesc = fraged_pages->pagedesc;
Size threshold;
FSMPageData *pageSpaces;
int outPages;
int i;
/*
* We only report pages with free space at least equal to the average
* request size --- this avoids cluttering FSM with uselessly-small bits
* of space. Although FSM would discard pages with little free space
* anyway, it's important to do this prefiltering because (a) it reduces
* the time spent holding the FSM lock in RecordRelationFreeSpace, and (b)
* FSM uses the number of pages reported as a statistic for guiding space
* management. If we didn't threshold our reports the same way
* vacuumlazy.c does, we'd be skewing that statistic.
*/
threshold = GetAvgFSMRequestSize(&onerel->rd_node);
pageSpaces = (FSMPageData *) palloc(nPages * sizeof(FSMPageData));
outPages = 0;
for (i = 0; i < nPages; i++)
{
/*
@ -3514,17 +3491,9 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages,
if (pagedesc[i]->blkno >= rel_pages)
break;
if (pagedesc[i]->free >= threshold)
{
FSMPageSetPageNum(&pageSpaces[outPages], pagedesc[i]->blkno);
FSMPageSetSpace(&pageSpaces[outPages], pagedesc[i]->free);
outPages++;
}
RecordPageWithFreeSpace(onerel, pagedesc[i]->blkno, pagedesc[i]->free);
}
RecordRelationFreeSpace(&onerel->rd_node, outPages, outPages, pageSpaces);
pfree(pageSpaces);
}
/* Copy a VacPage structure */

View File

@ -18,15 +18,6 @@
* index cleanup and page compaction, then resume the heap scan with an empty
* TID array.
*
* We can limit the storage for page free space to MaxFSMPages entries,
* since that's the most the free space map will be willing to remember
* anyway. If the relation has fewer than that many pages with free space,
* life is easy: just build an array of per-page info. If it has more,
* we store the free space info as a heap ordered by amount of free space,
* so that we can discard the pages with least free space to ensure we never
* have more than MaxFSMPages entries in all. The surviving page entries
* are passed to the free space map at conclusion of the scan.
*
* If we're processing a table with no indexes, we can just vacuum each page
* as we go; there's no need to save up multiple tuples to minimize the number
* of index scans performed. So we don't use maintenance_work_mem memory for
@ -38,7 +29,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.107 2008/05/12 00:00:48 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.108 2008/09/30 10:52:12 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -90,19 +81,11 @@ typedef struct LVRelStats
BlockNumber pages_removed;
double tuples_deleted;
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
Size threshold; /* minimum interesting free space */
/* List of TIDs of tuples we intend to delete */
/* NB: this list is ordered by TID address */
int num_dead_tuples; /* current # of entries */
int max_dead_tuples; /* # slots allocated in array */
ItemPointer dead_tuples; /* array of ItemPointerData */
/* Array or heap of per-page info about free space */
/* We use a simple array until it fills up, then convert to heap */
bool fs_is_heap; /* are we using heap organization? */
int num_free_pages; /* current # of entries */
int max_free_pages; /* # slots allocated in array */
FSMPageData *free_pages; /* array or heap of blkno/avail */
BlockNumber tot_free_pages; /* total pages with >= threshold space */
int num_index_scans;
} LVRelStats;
@ -134,12 +117,8 @@ static BlockNumber count_nondeletable_pages(Relation onerel,
static void lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks);
static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
ItemPointer itemptr);
static void lazy_record_free_space(LVRelStats *vacrelstats,
BlockNumber page, Size avail);
static bool lazy_tid_reaped(ItemPointer itemptr, void *state);
static void lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats);
static int vac_cmp_itemptr(const void *left, const void *right);
static int vac_cmp_page_spaces(const void *left, const void *right);
/*
@ -180,10 +159,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
/* Set threshold for interesting free space = average request size */
/* XXX should we scale it up or down? Adjust vacuum.c too, if so */
vacrelstats->threshold = GetAvgFSMRequestSize(&onerel->rd_node);
vacrelstats->num_index_scans = 0;
/* Open all indexes of the relation */
@ -207,18 +182,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION)
lazy_truncate_heap(onerel, vacrelstats);
/* Update shared free space map with final free space info */
lazy_update_fsm(onerel, vacrelstats);
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)),
RelationGetRelationName(onerel)),
/* Only suggest VACUUM FULL if > 20% free */
(vacrelstats->tot_free_pages > vacrelstats->rel_pages * 0.20) ?
errhint("Consider using VACUUM FULL on this relation or increasing the configuration parameter \"max_fsm_pages\".") :
errhint("Consider increasing the configuration parameter \"max_fsm_pages\".")));
/* Vacuum the Free Space Map */
FreeSpaceMapVacuum(onerel);
/* Update statistics in pg_class */
vac_update_relstats(RelationGetRelid(onerel),
@ -313,6 +278,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
int prev_dead_count;
OffsetNumber frozen[MaxOffsetNumber];
int nfrozen;
Size freespace;
vacuum_delay_point();
@ -375,20 +341,21 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
relname, blkno)));
PageInit(page, BufferGetPageSize(buf), 0);
empty_pages++;
lazy_record_free_space(vacrelstats, blkno,
PageGetHeapFreeSpace(page));
}
freespace = PageGetHeapFreeSpace(page);
MarkBufferDirty(buf);
UnlockReleaseBuffer(buf);
RecordPageWithFreeSpace(onerel, blkno, freespace);
continue;
}
if (PageIsEmpty(page))
{
empty_pages++;
lazy_record_free_space(vacrelstats, blkno,
PageGetHeapFreeSpace(page));
freespace = PageGetHeapFreeSpace(page);
UnlockReleaseBuffer(buf);
RecordPageWithFreeSpace(onerel, blkno, freespace);
continue;
}
@ -556,6 +523,14 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
vacuumed_pages++;
}
freespace = PageGetHeapFreeSpace(page);
/* Remember the location of the last page with nonremovable tuples */
if (hastup)
vacrelstats->nonempty_pages = blkno + 1;
UnlockReleaseBuffer(buf);
/*
* If we remembered any tuples for deletion, then the page will be
* visited again by lazy_vacuum_heap, which will compute and record
@ -564,16 +539,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
* taken if there are no indexes.)
*/
if (vacrelstats->num_dead_tuples == prev_dead_count)
{
lazy_record_free_space(vacrelstats, blkno,
PageGetHeapFreeSpace(page));
}
/* Remember the location of the last page with nonremovable tuples */
if (hastup)
vacrelstats->nonempty_pages = blkno + 1;
UnlockReleaseBuffer(buf);
RecordPageWithFreeSpace(onerel, blkno, freespace);
}
/* save stats for use later */
@ -611,12 +577,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
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))));
}
@ -649,6 +613,7 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats)
BlockNumber tblk;
Buffer buf;
Page page;
Size freespace;
vacuum_delay_point();
@ -656,11 +621,13 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats)
buf = ReadBufferWithStrategy(onerel, tblk, vac_strategy);
LockBufferForCleanup(buf);
tupindex = lazy_vacuum_page(onerel, tblk, buf, tupindex, vacrelstats);
/* Now that we've compacted the page, record its available space */
page = BufferGetPage(buf);
lazy_record_free_space(vacrelstats, tblk,
PageGetHeapFreeSpace(page));
freespace = PageGetHeapFreeSpace(page);
UnlockReleaseBuffer(buf);
RecordPageWithFreeSpace(onerel, tblk, freespace);
npages++;
}
@ -816,10 +783,6 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
{
BlockNumber old_rel_pages = vacrelstats->rel_pages;
BlockNumber new_rel_pages;
FSMPageData *pageSpaces;
int n;
int i,
j;
PGRUsage ru0;
pg_rusage_init(&ru0);
@ -865,6 +828,7 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
/*
* Okay to truncate.
*/
FreeSpaceMapTruncateRel(onerel, new_rel_pages);
RelationTruncate(onerel, new_rel_pages);
/*
@ -875,34 +839,6 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
* the table again.
*/
/*
* Drop free-space info for removed blocks; these must not get entered
* into the FSM!
*/
pageSpaces = vacrelstats->free_pages;
n = vacrelstats->num_free_pages;
j = 0;
for (i = 0; i < n; i++)
{
if (FSMPageGetPageNum(&pageSpaces[i]) < new_rel_pages)
{
pageSpaces[j] = pageSpaces[i];
j++;
}
}
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;
/* update statistics */
vacrelstats->rel_pages = new_rel_pages;
vacrelstats->pages_removed = old_rel_pages - new_rel_pages;
@ -1005,7 +941,6 @@ static void
lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)
{
long maxtuples;
int maxpages;
if (vacrelstats->hasindex)
{
@ -1029,19 +964,6 @@ lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)
vacrelstats->max_dead_tuples = (int) maxtuples;
vacrelstats->dead_tuples = (ItemPointer)
palloc(maxtuples * sizeof(ItemPointerData));
maxpages = MaxFSMPages;
maxpages = Min(maxpages, MaxAllocSize / sizeof(FSMPageData));
/* No need to allocate more pages than the relation has blocks */
if (relblocks < (BlockNumber) maxpages)
maxpages = (int) relblocks;
vacrelstats->fs_is_heap = false;
vacrelstats->num_free_pages = 0;
vacrelstats->max_free_pages = maxpages;
vacrelstats->free_pages = (FSMPageData *)
palloc(maxpages * sizeof(FSMPageData));
vacrelstats->tot_free_pages = 0;
}
/*
@ -1063,127 +985,6 @@ lazy_record_dead_tuple(LVRelStats *vacrelstats,
}
}
/*
* lazy_record_free_space - remember free space on one page
*/
static void
lazy_record_free_space(LVRelStats *vacrelstats,
BlockNumber page,
Size avail)
{
FSMPageData *pageSpaces;
int n;
/*
* A page with less than stats->threshold free space will be forgotten
* immediately, and never passed to the free space map. Removing the
* uselessly small entries early saves cycles, and in particular reduces
* the amount of time we spend holding the FSM lock when we finally call
* RecordRelationFreeSpace. Since the FSM will probably drop pages with
* little free space anyway, there's no point in making this really small.
*
* XXX Is it worth trying to measure average tuple size, and using that to
* adjust the threshold? Would be worthwhile if FSM has no stats yet for
* this relation. But changing the threshold as we scan the rel might
* lead to bizarre behavior, too. Also, it's probably better if vacuum.c
* has the same thresholding behavior as we do here.
*/
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;
/* If we haven't filled the array yet, just keep adding entries */
if (vacrelstats->num_free_pages < n)
{
FSMPageSetPageNum(&pageSpaces[vacrelstats->num_free_pages], page);
FSMPageSetSpace(&pageSpaces[vacrelstats->num_free_pages], avail);
vacrelstats->num_free_pages++;
return;
}
/*----------
* The rest of this routine works with "heap" organization of the
* free space arrays, wherein we maintain the heap property
* avail[(j-1) div 2] <= avail[j] for 0 < j < n.
* In particular, the zero'th element always has the smallest available
* space and can be discarded to make room for a new page with more space.
* See Knuth's discussion of heap-based priority queues, sec 5.2.3;
* but note he uses 1-origin array subscripts, not 0-origin.
*----------
*/
/* If we haven't yet converted the array to heap organization, do it */
if (!vacrelstats->fs_is_heap)
{
/*
* Scan backwards through the array, "sift-up" each value into its
* correct position. We can start the scan at n/2-1 since each entry
* above that position has no children to worry about.
*/
int l = n / 2;
while (--l >= 0)
{
BlockNumber R = FSMPageGetPageNum(&pageSpaces[l]);
Size K = FSMPageGetSpace(&pageSpaces[l]);
int i; /* i is where the "hole" is */
i = l;
for (;;)
{
int j = 2 * i + 1;
if (j >= n)
break;
if (j + 1 < n && FSMPageGetSpace(&pageSpaces[j]) > FSMPageGetSpace(&pageSpaces[j + 1]))
j++;
if (K <= FSMPageGetSpace(&pageSpaces[j]))
break;
pageSpaces[i] = pageSpaces[j];
i = j;
}
FSMPageSetPageNum(&pageSpaces[i], R);
FSMPageSetSpace(&pageSpaces[i], K);
}
vacrelstats->fs_is_heap = true;
}
/* If new page has more than zero'th entry, insert it into heap */
if (avail > FSMPageGetSpace(&pageSpaces[0]))
{
/*
* Notionally, we replace the zero'th entry with the new data, and
* then sift-up to maintain the heap property. Physically, the new
* data doesn't get stored into the arrays until we find the right
* location for it.
*/
int i = 0; /* i is where the "hole" is */
for (;;)
{
int j = 2 * i + 1;
if (j >= n)
break;
if (j + 1 < n && FSMPageGetSpace(&pageSpaces[j]) > FSMPageGetSpace(&pageSpaces[j + 1]))
j++;
if (avail <= FSMPageGetSpace(&pageSpaces[j]))
break;
pageSpaces[i] = pageSpaces[j];
i = j;
}
FSMPageSetPageNum(&pageSpaces[i], page);
FSMPageSetSpace(&pageSpaces[i], avail);
}
}
/*
* lazy_tid_reaped() -- is a particular tid deletable?
*
@ -1206,27 +1007,6 @@ lazy_tid_reaped(ItemPointer itemptr, void *state)
return (res != NULL);
}
/*
* Update the shared Free Space Map with the info we now have about
* free space in the relation, discarding any old info the map may have.
*/
static void
lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats)
{
FSMPageData *pageSpaces = vacrelstats->free_pages;
int nPages = vacrelstats->num_free_pages;
/*
* Sort data into order, as required by RecordRelationFreeSpace.
*/
if (nPages > 1)
qsort(pageSpaces, nPages, sizeof(FSMPageData),
vac_cmp_page_spaces);
RecordRelationFreeSpace(&onerel->rd_node, vacrelstats->tot_free_pages,
nPages, pageSpaces);
}
/*
* Comparator routines for use with qsort() and bsearch().
*/
@ -1256,18 +1036,3 @@ vac_cmp_itemptr(const void *left, const void *right)
return 0;
}
static int
vac_cmp_page_spaces(const void *left, const void *right)
{
FSMPageData *linfo = (FSMPageData *) left;
FSMPageData *rinfo = (FSMPageData *) right;
BlockNumber lblkno = FSMPageGetPageNum(linfo);
BlockNumber rblkno = FSMPageGetPageNum(rinfo);
if (lblkno < rblkno)
return -1;
else if (lblkno > rblkno)
return 1;
return 0;
}

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.51 2008/08/11 11:05:11 heikki Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.52 2008/09/30 10:52:13 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -55,7 +55,6 @@
#include "postmaster/bgwriter.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
@ -398,7 +397,6 @@ BackgroundWriterMain(void)
ExitOnAnyError = true;
/* Close down the database */
ShutdownXLOG(0, 0);
DumpFreeSpaceMap(0, 0);
/* Normal exit from the bgwriter is here */
proc_exit(0); /* done */
}

View File

@ -4,7 +4,7 @@
# Makefile for storage/freespace
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/storage/freespace/Makefile,v 1.4 2008/02/19 10:30:08 petere Exp $
# $PostgreSQL: pgsql/src/backend/storage/freespace/Makefile,v 1.5 2008/09/30 10:52:13 heikki Exp $
#
#-------------------------------------------------------------------------
@ -12,6 +12,6 @@ subdir = src/backend/storage/freespace
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = freespace.o
OBJS = freespace.o fsmpage.o indexfsm.o
include $(top_srcdir)/src/backend/common.mk

View File

@ -0,0 +1,195 @@
$PostgreSQL: pgsql/src/backend/storage/freespace/README,v 1.1 2008/09/30 10:52:13 heikki Exp $
Free Space Map
--------------
The purpose of the free space map is to quickly locate a page with enough
free space to hold a tuple to be stored; or to determine that no such page
exists and the relation must be extended by one page. As of PostgreSQL 8.4
each relation has its own, extensible free space map stored in a separate
"fork" of its relation. This eliminates the disadvantages of the former
fixed-size FSM.
It is important to keep the map small so that it can be searched rapidly.
Therefore, we don't attempt to record the exact free space on a page.
We allocate one map byte to each page, allowing us to record free space
at a granularity of 1/256th of a page. Another way to say it is that
the stored value is the free space divided by BLCKSZ/256 (rounding down).
We assume that the free space must always be less than BLCKSZ, since
all pages have some overhead; so the maximum map value is 255.
To assist in fast searching, the map isn't simply an array of per-page
entries, but has a tree structure above those entries. There is a tree
structure of pages, and a tree structure within each page, as described
below.
FSM page structure
------------------
Within each FSM page, we use a binary tree structure where leaf nodes store
the amount of free space on heap pages (or lower level FSM pages, see
"Higher-level structure" below), with one leaf node per heap page. A non-leaf
node stores the max amount of free space on any of its children.
For example:
4
4 2
3 4 0 2 <- This level represents heap pages
We need two basic operations: search and update.
To search for a page with X amount of free space, traverse down the tree
along a path where n >= X, until you hit the bottom. If both children of a
node satisfy the condition, you can pick either one arbitrarily.
To update the amount of free space on a page to X, first update the leaf node
corresponding to the heap page, then "bubble up" the change to upper nodes,
by walking up to each parent and recomputing its value as the max of its
two children. Repeat until reaching the root or a parent whose value
doesn't change.
This data structure has a couple of nice properties:
- to discover that there is no page with X bytes of free space, you only
need to look at the root node
- by varying which child to traverse to in the search algorithm, when you have
a choice, we can implement various strategies, like preferring pages closer
to a given page, or spreading the load across the table.
Higher-level routines that use FSM pages access them through the fsm_set_avail()
and fsm_search_avail() functions. The interface to those functions hides the
page's internal tree structure, treating the FSM page as a black box that has
a certain number of "slots" for storing free space information. (However,
the higher routines have to be aware of the tree structure of the whole map.)
The binary tree is stored on each FSM page as an array. Because the page
header takes some space on a page, the binary tree isn't perfect. That is,
a few right-most leaf nodes are missing, and there are some useless non-leaf
nodes at the right. So the tree looks something like this:
0
1 2
3 4 5 6
7 8 9 A B
where the numbers denote each node's position in the array. Note that the
tree is guaranteed complete above the leaf level; only some leaf nodes are
missing. This is reflected in the number of usable "slots" per page not
being an exact power of 2.
A FSM page also has a next slot pointer, fp_next_slot, that determines where
to start the next search for free space within that page. The reason for that
is to spread out the pages that are returned by FSM searches. When several
backends are concurrently inserting into a relation, contention can be avoided
by having them insert into different pages. But it is also desirable to fill
up pages in sequential order, to get the benefit of OS prefetching and batched
writes. The FSM is responsible for making that happen, and the next slot
pointer helps provide the desired behavior.
Higher-level structure
----------------------
To scale up the data structure described above beyond a single page, we
maintain a similar tree-structure across pages. Leaf nodes in higher level
pages correspond to lower level FSM pages. The root node within each page
has the same value as the corresponding leaf node on its parent page.
The root page is always stored at physical block 0.
For example, assuming each FSM page can hold information about 4 pages (in
reality, it holds (BLCKSZ - headers) / 2, or ~4000 with default BLCKSZ),
we get a disk layout like this:
0 <-- page 0 at level 2 (root page)
0 <-- page 0 at level 1
0 <-- page 0 at level 0
1 <-- page 1 at level 0
2 <-- ...
3
1 <-- page 1 at level 1
4
5
6
7
2
8
9
10
11
3
12
13
14
15
where the numbers are page numbers *at that level*, starting from 0.
To find the physical block # corresponding to leaf page n, we need to
count the number number of leaf and upper-level pages preceding page n.
This turns out to be
y = n + (n / F + 1) + (n / F^2 + 1) + ... + 1
where F is the fanout (4 in the above example). The first term n is the number
of preceding leaf pages, the second term is the number of pages at level 1,
and so forth.
To keep things simple, the tree is always constant height. To cover the
maximum relation size of 2^32-1 blocks, three levels is enough with the default
BLCKSZ (4000^3 > 2^32).
Addressing
----------
The higher-level routines operate on "logical" addresses, consisting of
- level,
- logical page number, and
- slot (if applicable)
Bottom level FSM pages have level of 0, the level above that 1, and root 2.
As in the diagram above, logical page number is the page number at that level,
starting from 0.
Locking
-------
When traversing down to search for free space, only one page is locked at a
time: the parent page is released before locking the child. If the child page
is concurrently modified, and there no longer is free space on the child page
when you land on it, you need to start from scratch (after correcting the
parent page, so that you don't get into an infinite loop).
We use shared buffer locks when searching, but exclusive buffer lock when
updating a page. However, the next slot search pointer is updated during
searches even though we have only a shared lock. fp_next_slot is just a hint
and we can easily reset it if it gets corrupted; so it seems better to accept
some risk of that type than to pay the overhead of exclusive locking.
Recovery
--------
The FSM is not explicitly WAL-logged. Instead, we rely on a bunch of
self-correcting measures to repair possible corruption.
First of all, whenever a value is set on an FSM page, the root node of the
page is compared against the new value after bubbling up the change is
finished. It should be greater than or equal to the value just set, or we
have a corrupted page, with a parent somewhere with too small a value.
Secondly, if we detect corrupted pages while we search, traversing down
the tree. That check will notice if a parent node is set to too high a value.
In both cases, the upper nodes on the page are immediately rebuilt, fixing
the corruption.
Vacuum updates all the bottom level pages with correct amount of free space
on the heap pages, fixing any outdated values there. After the heap and
index passes are done, FreeSpaceMapVacuum is called, and the FSM tree is
scanned in depth-first order. This fixes any discrepancies between upper
and lower level FSM pages.
TODO
----
- fastroot to avoid traversing upper nodes with just 1 child
- use a different system for tables that fit into one FSM page, with a
mechanism to switch to the real thing as it grows.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,352 @@
/*-------------------------------------------------------------------------
*
* fsmpage.c
* routines to search and manipulate one FSM page.
*
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/freespace/fsmpage.c,v 1.1 2008/09/30 10:52:13 heikki Exp $
*
* NOTES:
*
* The public functions in this file form an API that hides the internal
* structure of a FSM page. This allows freespace.c to treat each FSM page
* as a black box with SlotsPerPage "slots". fsm_set_avail() and
* fsm_get_avail() let's you get/set the value of a slot, and
* fsm_search_avail() let's you search for a slot with value >= X.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "storage/bufmgr.h"
#include "storage/fsm_internals.h"
/* macros to navigate the tree within a page. */
#define leftchild(x) (2 * (x) + 1)
#define rightchild(x) (2 * (x) + 2)
#define parentof(x) (((x) - 1) / 2)
/* returns right sibling of x, wrapping around within the level */
static int
rightsibling(int x)
{
/*
* Move right. This might wrap around, stepping to the leftmost node at
* the next level.
*/
x++;
/*
* Check if we stepped to the leftmost node at next level, and correct
* if so. The leftmost nodes at each level are of form x = 2^level - 1, so
* check if (x + 1) is a power of two.
*/
if (((x + 1) & x) == 0)
x = parentof(x);
return x;
}
/*
* Sets the value of a slot on page. Returns true if the page was
* modified.
*
* The caller must hold an exclusive lock on the page.
*/
bool
fsm_set_avail(Page page, int slot, uint8 value)
{
int nodeno = NonLeafNodesPerPage + slot;
FSMPage fsmpage = (FSMPage) PageGetContents(page);
uint8 oldvalue;
Assert(slot < LeafNodesPerPage);
oldvalue = fsmpage->fp_nodes[nodeno];
/* If the value hasn't changed, we don't need to do anything */
if (oldvalue == value && value <= fsmpage->fp_nodes[0])
return false;
fsmpage->fp_nodes[nodeno] = value;
/*
* Propagate up, until we hit the root or a node that doesn't
* need to be updated.
*/
do
{
uint8 newvalue = 0;
int lchild;
int rchild;
nodeno = parentof(nodeno);
lchild = leftchild(nodeno);
rchild = lchild + 1;
newvalue = fsmpage->fp_nodes[lchild];
if (rchild < NodesPerPage)
newvalue = Max(newvalue,
fsmpage->fp_nodes[rchild]);
oldvalue = fsmpage->fp_nodes[nodeno];
if (oldvalue == newvalue)
break;
fsmpage->fp_nodes[nodeno] = newvalue;
} while (nodeno > 0);
/*
* sanity check: if the new value value is higher than the value
* at the top, the tree is corrupt.
*/
if (value > fsmpage->fp_nodes[0])
fsm_rebuild_page(page);
return true;
}
/*
* Returns the value of given slot on page.
*
* Since this is just a read-only access of a single byte, the page doesn't
* need to be locked.
*/
uint8
fsm_get_avail(Page page, int slot)
{
FSMPage fsmpage = (FSMPage) PageGetContents(page);
return fsmpage->fp_nodes[NonLeafNodesPerPage + slot];
}
/*
* Returns the value at the root of a page.
* Since this is just a read-only access of a single byte, the page doesn't
* need to be locked.
*/
uint8
fsm_get_max_avail(Page page)
{
FSMPage fsmpage = (FSMPage) PageGetContents(page);
return fsmpage->fp_nodes[0];
}
/*
* Searches for a slot with min. category. Returns slot number, or -1 if
* none found.
*
* The caller must hold at least a shared lock on the page, and this
* function can unlock and lock the page again in exclusive mode if it
* needs to be updated. exclusive_lock_held should be set to true if the
* caller is already holding an exclusive lock, to avoid extra work.
*
* If advancenext is false, fp_next_slot is set to point to the returned
* slot, and if it's true, to the slot next to the returned slot.
*/
int
fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext,
bool exclusive_lock_held)
{
Page page = BufferGetPage(buf);
FSMPage fsmpage = (FSMPage) PageGetContents(page);
int nodeno;
int target;
uint16 slot;
restart:
/*
* Check the root first, and exit quickly if there's no page with
* enough free space
*/
if (fsmpage->fp_nodes[0] < minvalue)
return -1;
/* fp_next_slot is just a hint, so check that it's sane */
target = fsmpage->fp_next_slot;
if (target < 0 || target >= LeafNodesPerPage)
target = 0;
target += NonLeafNodesPerPage;
/*
* Start the search from the target slot. At every step, move one
* node to the right, and climb up to the parent. Stop when we reach a
* node with enough free space. (note that moving to the right only
* makes a difference if we're on the right child of the parent)
*
* The idea is to graduall expand our "search triangle", that is, all
* nodes covered by the current node. In the beginning, just the target
* node is included, and more nodes to the right of the target node,
* taking wrap-around into account, is included at each step. Nodes are
* added to the search triangle in left-to-right order, starting from
* the target node. This ensures that we'll find the first suitable node
* to the right of the target node, and not some other node with enough
* free space.
*
* For example, consider this tree:
*
* 7
* 7 6
* 5 7 6 5
* 4 5 5 7 2 6 5 2
* T
*
* Imagine that target node is the node indicated by the letter T, and
* we're searching for a node with value of 6 or higher. The search
* begins at T. At first iteration, we move to the right, and to the
* parent, arriving the rightmost 5. At the 2nd iteration, we move to the
* right, wrapping around, and climb up, arriving at the 7 at the 2nd
* level. 7 satisfies our search, so we descend down to the bottom,
* following the path of sevens.
*/
nodeno = target;
while (nodeno > 0)
{
if (fsmpage->fp_nodes[nodeno] >= minvalue)
break;
/*
* Move to the right, wrapping around at the level if necessary, and
* climb up.
*/
nodeno = parentof(rightsibling(nodeno));
}
/*
* We're now at a node with enough free space, somewhere in the middle of
* the tree. Descend to the bottom, following a path with enough free
* space, preferring to move left if there's a choice.
*/
while (nodeno < NonLeafNodesPerPage)
{
int leftnodeno = leftchild(nodeno);
int rightnodeno = leftnodeno + 1;
bool leftok = (leftnodeno < NodesPerPage) &&
(fsmpage->fp_nodes[leftnodeno] >= minvalue);
bool rightok = (rightnodeno < NodesPerPage) &&
(fsmpage->fp_nodes[rightnodeno] >= minvalue);
if (leftok)
nodeno = leftnodeno;
else if (rightok)
nodeno = rightnodeno;
else
{
/*
* Oops. The parent node promised that either left or right
* child has enough space, but neither actually did. This can
* happen in case of a "torn page", IOW if we crashed earlier
* while writing the page to disk, and only part of the page
* made it to disk.
*
* Fix the corruption and restart.
*/
RelFileNode rnode;
ForkNumber forknum;
BlockNumber blknum;
BufferGetTag(buf, &rnode, &forknum, &blknum);
elog(DEBUG1, "fixing corrupt FSM block %u, relation %u/%u/%u",
blknum, rnode.spcNode, rnode.dbNode, rnode.relNode);
/* make sure we hold an exclusive lock */
if (!exclusive_lock_held)
{
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
exclusive_lock_held = true;
}
fsm_rebuild_page(page);
MarkBufferDirty(buf);
goto restart;
}
}
/* We're now at the bottom level, at a node with enough space. */
slot = nodeno - NonLeafNodesPerPage;
/*
* Update the next slot pointer. Note that we do this even if we're only
* holding a shared lock, on the grounds that it's better to use a shared
* lock and get a garbled next pointer every now and then, than take the
* concurrency hit of an exlusive lock.
*
* Wrap-around is handled at the beginning of this function.
*/
fsmpage->fp_next_slot = slot + (advancenext ? 1 : 0);
return slot;
}
/*
* Sets the available space to zero for all slots numbered >= nslots.
* Returns true if the page was modified.
*/
bool
fsm_truncate_avail(Page page, int nslots)
{
FSMPage fsmpage = (FSMPage) PageGetContents(page);
uint8 *ptr;
bool changed = false;
Assert(nslots >= 0 && nslots < LeafNodesPerPage);
/* Clear all truncated leaf nodes */
ptr = &fsmpage->fp_nodes[NonLeafNodesPerPage + nslots];
for (; ptr < &fsmpage->fp_nodes[NodesPerPage]; ptr++)
{
if (*ptr != 0)
changed = true;
*ptr = 0;
}
/* Fix upper nodes. */
if (changed)
fsm_rebuild_page(page);
return changed;
}
/*
* Reconstructs the upper levels of a page. Returns true if the page
* was modified.
*/
bool
fsm_rebuild_page(Page page)
{
FSMPage fsmpage = (FSMPage) PageGetContents(page);
bool changed = false;
int nodeno;
/*
* Start from the lowest non-leaflevel, at last node, working our way
* backwards, through all non-leaf nodes at all levels, up to the root.
*/
for (nodeno = NonLeafNodesPerPage - 1; nodeno >= 0; nodeno--)
{
int lchild = leftchild(nodeno);
int rchild = lchild + 1;
uint8 newvalue = 0;
if (lchild < NodesPerPage)
newvalue = fsmpage->fp_nodes[lchild];
if (rchild < NodesPerPage)
newvalue = Max(newvalue,
fsmpage->fp_nodes[rchild]);
if (fsmpage->fp_nodes[nodeno] != newvalue)
{
fsmpage->fp_nodes[nodeno] = newvalue;
changed = true;
}
}
return changed;
}

View File

@ -0,0 +1,92 @@
/*-------------------------------------------------------------------------
*
* indexfsm.c
* POSTGRES free space map for quickly finding free pages in relations
*
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/freespace/indexfsm.c,v 1.1 2008/09/30 10:52:13 heikki Exp $
*
*
* NOTES:
*
* This is similar to the FSM used for heap, in freespace.c, but instead
* of tracking the amount of free space on pages, we only track whether
* pages are completely free or in-use. We use the same FSM implementation
* as for heaps, using BLCKSZ - 1 to denote used pages, and 0 for unused.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/smgr.h"
/*
* Exported routines
*/
/*
* InitIndexFreeSpaceMap - Create or reset the FSM fork for relation.
*/
void
InitIndexFreeSpaceMap(Relation rel)
{
/* Create FSM fork if it doesn't exist yet, or truncate it if it does */
RelationOpenSmgr(rel);
if (!smgrexists(rel->rd_smgr, FSM_FORKNUM))
smgrcreate(rel->rd_smgr, FSM_FORKNUM, rel->rd_istemp, false);
else
smgrtruncate(rel->rd_smgr, FSM_FORKNUM, 0, rel->rd_istemp);
}
/*
* GetFreeIndexPage - return a free page from the FSM
*
* As a side effect, the page is marked as used in the FSM.
*/
BlockNumber
GetFreeIndexPage(Relation rel)
{
BlockNumber blkno = GetPageWithFreeSpace(rel, BLCKSZ/2);
if (blkno != InvalidBlockNumber)
RecordUsedIndexPage(rel, blkno);
return blkno;
}
/*
* RecordFreeIndexPage - mark a page as free in the FSM
*/
void
RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
{
RecordPageWithFreeSpace(rel, freeBlock, BLCKSZ - 1);
}
/*
* RecordUsedIndexPage - mark a page as used in the FSM
*/
void
RecordUsedIndexPage(Relation rel, BlockNumber usedBlock)
{
RecordPageWithFreeSpace(rel, usedBlock, 0);
}
/*
* IndexFreeSpaceMapTruncate - adjust for truncation of a relation.
*
* We need to delete any stored data past the new relation length, so that
* we don't bogusly return removed block numbers.
*/
void
IndexFreeSpaceMapTruncate(Relation rel, BlockNumber nblocks)
{
FreeSpaceMapTruncateRel(rel, nblocks);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.96 2008/05/12 00:00:50 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.97 2008/09/30 10:52:13 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,7 +26,6 @@
#include "postmaster/bgwriter.h"
#include "postmaster/postmaster.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
@ -110,7 +109,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
size = add_size(size, ProcArrayShmemSize());
size = add_size(size, BackendStatusShmemSize());
size = add_size(size, SInvalShmemSize());
size = add_size(size, FreeSpaceShmemSize());
size = add_size(size, BgWriterShmemSize());
size = add_size(size, AutoVacuumShmemSize());
size = add_size(size, BTreeShmemSize());
@ -203,11 +201,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
*/
CreateSharedInvalidationState();
/*
* Set up free-space map
*/
InitFreeSpaceMap();
/*
* Set up interprocess signaling mechanisms
*/

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.111 2008/08/11 11:05:11 heikki Exp $
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.112 2008/09/30 10:52:13 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,7 +21,6 @@
#include "access/xlogutils.h"
#include "commands/tablespace.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/smgr.h"
#include "utils/hsearch.h"
@ -474,13 +473,6 @@ smgr_internal_unlink(RelFileNode rnode, ForkNumber forknum,
*/
DropRelFileNodeBuffers(rnode, forknum, isTemp, 0);
/*
* Tell the free space map to forget this relation. It won't be accessed
* any more anyway, but we may as well recycle the map space quickly.
*/
if (forknum == MAIN_FORKNUM)
FreeSpaceMapForgetRel(&rnode);
/*
* It'd be nice to tell the stats collector to forget it immediately, too.
* But we can't because we don't know the OID (and in cases involving
@ -577,13 +569,6 @@ smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks,
*/
DropRelFileNodeBuffers(reln->smgr_rnode, forknum, isTemp, nblocks);
/*
* Tell the free space map to forget anything it may have stored for the
* about-to-be-deleted blocks. We want to be sure it won't return bogus
* block numbers later on.
*/
FreeSpaceMapTruncateRel(&reln->smgr_rnode, nblocks);
/* Do the truncation */
(*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, forknum, nblocks,
isTemp);
@ -905,13 +890,6 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
DropRelFileNodeBuffers(xlrec->rnode, xlrec->forknum, false,
xlrec->blkno);
/*
* Tell the free space map to forget anything it may have stored for
* the about-to-be-deleted blocks. We want to be sure it won't return
* bogus block numbers later on.
*/
FreeSpaceMapTruncateRel(&reln->smgr_rnode, xlrec->blkno);
/* Do the truncation */
(*(smgrsw[reln->smgr_which].smgr_truncate)) (reln,
xlrec->forknum,

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.556 2008/08/19 18:30:04 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.557 2008/09/30 10:52:13 heikki Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@ -57,7 +57,6 @@
#include "postmaster/autovacuum.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/sinval.h"
@ -3258,13 +3257,6 @@ PostgresMain(int argc, char *argv[], const char *username)
StartupXLOG();
on_shmem_exit(ShutdownXLOG, 0);
/*
* Read any existing FSM cache file, and register to write one out at
* exit.
*/
LoadFreeSpaceMap();
on_shmem_exit(DumpFreeSpaceMap, 0);
/*
* We have to build the flat file for pg_database, but not for the
* user and group tables, since we won't try to do authentication.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.273 2008/08/10 19:02:33 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.274 2008/09/30 10:52:13 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -304,6 +304,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
*/
MemSet(relation, 0, sizeof(RelationData));
relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */
relation->rd_smgr = NULL;
@ -1364,6 +1365,7 @@ formrdesc(const char *relationName, Oid relationReltype,
*/
relation = (Relation) palloc0(sizeof(RelationData));
relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */
relation->rd_smgr = NULL;
@ -1652,8 +1654,9 @@ RelationReloadIndexInfo(Relation relation)
heap_freetuple(pg_class_tuple);
/* We must recalculate physical address in case it changed */
RelationInitPhysicalAddr(relation);
/* Make sure targblock is reset in case rel was truncated */
/* Must reset targblock and fsm_nblocks_cache in case rel was truncated */
relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* Must free any AM cached data, too */
if (relation->rd_amcache)
pfree(relation->rd_amcache);
@ -1736,6 +1739,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (relation->rd_isnailed)
{
relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
if (relation->rd_rel->relkind == RELKIND_INDEX)
{
relation->rd_isvalid = false; /* needs to be revalidated */
@ -2330,6 +2334,7 @@ RelationBuildLocalRelation(const char *relname,
rel = (Relation) palloc0(sizeof(RelationData));
rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */
rel->rd_smgr = NULL;
@ -3586,6 +3591,7 @@ load_relcache_init_file(void)
*/
rel->rd_smgr = NULL;
rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks_cache = InvalidBlockNumber;
if (rel->rd_isnailed)
rel->rd_refcnt = 1;
else

View File

@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.473 2008/09/23 21:12:03 mha Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.474 2008/09/30 10:52:13 heikki Exp $
*
*--------------------------------------------------------------------
*/
@ -57,7 +57,6 @@
#include "regex/regex.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/freespace.h"
#include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h"
#include "utils/builtins.h"
@ -446,8 +445,6 @@ const char *const config_group_names[] =
gettext_noop("Resource Usage"),
/* RESOURCES_MEM */
gettext_noop("Resource Usage / Memory"),
/* RESOURCES_FSM */
gettext_noop("Resource Usage / Free Space Map"),
/* RESOURCES_KERNEL */
gettext_noop("Resource Usage / Kernel Resources"),
/* WAL */
@ -1528,23 +1525,6 @@ static struct config_int ConfigureNamesInt[] =
100000000, 0, 1000000000, NULL, NULL
},
{
{"max_fsm_relations", PGC_POSTMASTER, RESOURCES_FSM,
gettext_noop("Sets the maximum number of tables and indexes for which free space is tracked."),
NULL
},
&MaxFSMRelations,
1000, 100, INT_MAX, NULL, NULL
},
{
{"max_fsm_pages", PGC_POSTMASTER, RESOURCES_FSM,
gettext_noop("Sets the maximum number of disk pages for which free space is tracked."),
NULL
},
&MaxFSMPages,
20000, 1000, INT_MAX, NULL, NULL
},
{
{"max_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT,
gettext_noop("Sets the maximum number of locks per transaction."),

View File

@ -114,13 +114,6 @@
#maintenance_work_mem = 16MB # min 1MB
#max_stack_depth = 2MB # min 100kB
# - Free Space Map -
#max_fsm_pages = 204800 # min max_fsm_relations*16, 6 bytes each
# (change requires restart)
#max_fsm_relations = 1000 # min 100, ~70 bytes each
# (change requires restart)
# - Kernel Resource Usage -
#max_files_per_process = 1000 # min 25

View File

@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD.
*
* $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.161 2008/09/23 10:58:03 heikki Exp $
* $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.162 2008/09/30 10:52:13 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -119,7 +119,6 @@ static int output_errno = 0;
/* defaults */
static int n_connections = 10;
static int n_buffers = 50;
static int n_fsm_pages = 20000;
/*
* Warning messages for authentication methods
@ -1041,13 +1040,10 @@ static void
test_config_settings(void)
{
/*
* These macros define the minimum shared_buffers we want for a given
* max_connections value, and the max_fsm_pages setting to be used for a
* given shared_buffers value. The arrays show the settings to try.
* This macro defines the minimum shared_buffers we want for a given
* max_connections value. The arrays show the settings to try.
*/
#define MIN_BUFS_FOR_CONNS(nconns) ((nconns) * 10)
#define FSM_FOR_BUFS(nbuffers) ((nbuffers) > 1000 ? 50 * (nbuffers) : 20000)
static const int trial_conns[] = {
100, 50, 40, 30, 20, 10
@ -1065,7 +1061,6 @@ test_config_settings(void)
status,
test_conns,
test_buffs,
test_max_fsm,
ok_buffers = 0;
@ -1076,16 +1071,14 @@ test_config_settings(void)
{
test_conns = trial_conns[i];
test_buffs = MIN_BUFS_FOR_CONNS(test_conns);
test_max_fsm = FSM_FOR_BUFS(test_buffs);
snprintf(cmd, sizeof(cmd),
SYSTEMQUOTE "\"%s\" --boot -x0 %s "
"-c max_connections=%d "
"-c shared_buffers=%d "
"-c max_fsm_pages=%d "
"< \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
backend_exec, boot_options,
test_conns, test_buffs, test_max_fsm,
test_conns, test_buffs,
DEVNULL, DEVNULL);
status = system(cmd);
if (status == 0)
@ -1100,7 +1093,7 @@ test_config_settings(void)
printf("%d\n", n_connections);
printf(_("selecting default shared_buffers/max_fsm_pages ... "));
printf(_("selecting default shared_buffers ... "));
fflush(stdout);
for (i = 0; i < bufslen; i++)
@ -1112,28 +1105,25 @@ test_config_settings(void)
test_buffs = ok_buffers;
break;
}
test_max_fsm = FSM_FOR_BUFS(test_buffs);
snprintf(cmd, sizeof(cmd),
SYSTEMQUOTE "\"%s\" --boot -x0 %s "
"-c max_connections=%d "
"-c shared_buffers=%d "
"-c max_fsm_pages=%d "
"< \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
backend_exec, boot_options,
n_connections, test_buffs, test_max_fsm,
n_connections, test_buffs,
DEVNULL, DEVNULL);
status = system(cmd);
if (status == 0)
break;
}
n_buffers = test_buffs;
n_fsm_pages = FSM_FOR_BUFS(n_buffers);
if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
printf("%dMB/%d\n", (n_buffers * (BLCKSZ / 1024)) / 1024, n_fsm_pages);
printf("%dMB\n", (n_buffers * (BLCKSZ / 1024)) / 1024);
else
printf("%dkB/%d\n", n_buffers * (BLCKSZ / 1024), n_fsm_pages);
printf("%dkB\n", n_buffers * (BLCKSZ / 1024));
}
/*
@ -1164,9 +1154,6 @@ setup_config(void)
n_buffers * (BLCKSZ / 1024));
conflines = replace_token(conflines, "#shared_buffers = 32MB", repltok);
snprintf(repltok, sizeof(repltok), "max_fsm_pages = %d", n_fsm_pages);
conflines = replace_token(conflines, "#max_fsm_pages = 204800", repltok);
#if DEF_PGPORT != 5432
snprintf(repltok, sizeof(repltok), "#port = %d", DEF_PGPORT);
conflines = replace_token(conflines, "#port = 5432", repltok);

View File

@ -3,7 +3,7 @@
*
* Resource managers definition
*
* $PostgreSQL: pgsql/src/include/access/rmgr.h,v 1.17 2006/11/05 22:42:10 tgl Exp $
* $PostgreSQL: pgsql/src/include/access/rmgr.h,v 1.18 2008/09/30 10:52:13 heikki Exp $
*/
#ifndef RMGR_H
#define RMGR_H
@ -23,6 +23,7 @@ typedef uint8 RmgrId;
#define RM_DBASE_ID 4
#define RM_TBLSPC_ID 5
#define RM_MULTIXACT_ID 6
#define RM_FREESPACE_ID 7
#define RM_HEAP2_ID 9
#define RM_HEAP_ID 10
#define RM_BTREE_ID 11

View File

@ -7,152 +7,32 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/freespace.h,v 1.28 2008/03/10 02:04:10 tgl Exp $
* $PostgreSQL: pgsql/src/include/storage/freespace.h,v 1.29 2008/09/30 10:52:13 heikki Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef FREESPACE_H_
#define FREESPACE_H_
#include "storage/relfilenode.h"
#include "storage/itemptr.h"
#include "utils/rel.h"
#include "storage/bufpage.h"
#include "access/xlog.h"
/* Initial value for average-request moving average */
#define INITIAL_AVERAGE ((Size) (BLCKSZ / 32))
/*
* Number of pages and bytes per allocation chunk. Indexes can squeeze 50%
* more pages into the same space because they don't need to remember how much
* free space on each page. The nominal number of pages, CHUNKPAGES, is for
* regular rels, and INDEXCHUNKPAGES is for indexes. CHUNKPAGES should be
* even so that no space is wasted in the index case.
*/
#define CHUNKPAGES 16
#define CHUNKBYTES (CHUNKPAGES * sizeof(FSMPageData))
#define INDEXCHUNKPAGES ((int) (CHUNKBYTES / sizeof(IndexFSMPageData)))
/*
* Typedefs and macros for items in the page-storage arena. We use the
* existing ItemPointer and BlockId data structures, which are designed
* to pack well (they should be 6 and 4 bytes apiece regardless of machine
* alignment issues). Unfortunately we can't use the ItemPointer access
* macros, because they include Asserts insisting that ip_posid != 0.
*/
typedef ItemPointerData FSMPageData;
typedef BlockIdData IndexFSMPageData;
#define FSMPageGetPageNum(ptr) \
BlockIdGetBlockNumber(&(ptr)->ip_blkid)
#define FSMPageGetSpace(ptr) \
((Size) (ptr)->ip_posid)
#define FSMPageSetPageNum(ptr, pg) \
BlockIdSet(&(ptr)->ip_blkid, pg)
#define FSMPageSetSpace(ptr, sz) \
((ptr)->ip_posid = (OffsetNumber) (sz))
#define IndexFSMPageGetPageNum(ptr) \
BlockIdGetBlockNumber(ptr)
#define IndexFSMPageSetPageNum(ptr, pg) \
BlockIdSet(ptr, pg)
/*
* Shared free-space-map objects
*
* The per-relation objects are indexed by a hash table, and are also members
* of two linked lists: one ordered by recency of usage (most recent first),
* and the other ordered by physical location of the associated storage in
* the page-info arena.
*
* Each relation owns one or more chunks of per-page storage in the "arena".
* The chunks for each relation are always consecutive, so that it can treat
* its page storage as a simple array. We further insist that its page data
* be ordered by block number, so that binary search is possible.
*
* Note: we handle pointers to these items as pointers, not as SHMEM_OFFSETs.
* This assumes that all processes accessing the map will have the shared
* memory segment mapped at the same place in their address space.
*/
typedef struct FSMHeader FSMHeader;
typedef struct FSMRelation FSMRelation;
/* Header for whole map */
struct FSMHeader
{
FSMRelation *usageList; /* FSMRelations in usage-recency order */
FSMRelation *usageListTail; /* tail of usage-recency list */
FSMRelation *firstRel; /* FSMRelations in arena storage order */
FSMRelation *lastRel; /* tail of storage-order list */
int numRels; /* number of FSMRelations now in use */
double sumRequests; /* sum of requested chunks over all rels */
char *arena; /* arena for page-info storage */
int totalChunks; /* total size of arena, in chunks */
int usedChunks; /* # of chunks assigned */
/* NB: there are totalChunks - usedChunks free chunks at end of arena */
};
/*
* Per-relation struct --- this is an entry in the shared hash table.
* The hash key is the RelFileNode value (hence, we look at the physical
* relation ID, not the logical ID, which is appropriate).
*/
struct FSMRelation
{
RelFileNode key; /* hash key (must be first) */
FSMRelation *nextUsage; /* next rel in usage-recency order */
FSMRelation *priorUsage; /* prior rel in usage-recency order */
FSMRelation *nextPhysical; /* next rel in arena-storage order */
FSMRelation *priorPhysical; /* prior rel in arena-storage order */
bool isIndex; /* if true, we store only page numbers */
Size avgRequest; /* moving average of space requests */
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 */
};
/* GUC variables */
extern PGDLLIMPORT int MaxFSMRelations;
extern PGDLLIMPORT int MaxFSMPages;
/*
* function prototypes
*/
extern void InitFreeSpaceMap(void);
extern Size FreeSpaceShmemSize(void);
extern FSMHeader *GetFreeSpaceMap(void);
extern BlockNumber GetPageWithFreeSpace(RelFileNode *rel, Size spaceNeeded);
extern BlockNumber RecordAndGetPageWithFreeSpace(RelFileNode *rel,
/* prototypes for public functions in freespace.c */
extern Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk);
extern BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded);
extern BlockNumber RecordAndGetPageWithFreeSpace(Relation rel,
BlockNumber oldPage,
Size oldSpaceAvail,
Size spaceNeeded);
extern Size GetAvgFSMRequestSize(RelFileNode *rel);
extern void RecordRelationFreeSpace(RelFileNode *rel,
BlockNumber interestingPages,
int nPages,
FSMPageData *pageSpaces);
extern void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk,
Size spaceAvail);
extern BlockNumber GetFreeIndexPage(RelFileNode *rel);
extern void RecordIndexFreeSpace(RelFileNode *rel,
BlockNumber interestingPages,
int nPages,
BlockNumber *pages);
extern void FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks);
extern void FreeSpaceMapVacuum(Relation rel);
extern void FreeSpaceMapTruncateRel(RelFileNode *rel, BlockNumber nblocks);
extern void FreeSpaceMapForgetRel(RelFileNode *rel);
extern void FreeSpaceMapForgetDatabase(Oid dbid);
extern void PrintFreeSpaceMapStatistics(int elevel);
extern void DumpFreeSpaceMap(int code, Datum arg);
extern void LoadFreeSpaceMap(void);
#ifdef FREESPACE_DEBUG
extern void DumpFreeSpace(void);
#endif
/* WAL prototypes */
extern void fsm_desc(StringInfo buf, uint8 xl_info, char *rec);
extern void fsm_redo(XLogRecPtr lsn, XLogRecord *record);
#endif /* FREESPACE_H */

View File

@ -0,0 +1,73 @@
/*-------------------------------------------------------------------------
*
* fsm_internal.h
* internal functions for free space map
*
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/fsm_internals.h,v 1.1 2008/09/30 10:52:14 heikki Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef FSM_INTERNALS_H
#define FSM_INTERNALS_H
#include "storage/buf.h"
#include "storage/bufpage.h"
#include "lib/stringinfo.h"
/*
* Structure of a FSM page. See src/backend/storage/freespace/README for
* details.
*/
typedef struct
{
/*
* fsm_search_avail() tries to spread the load of multiple backends
* by returning different pages to different backends in a round-robin
* fashion. fp_next_slot points to the next slot to be returned
* (assuming there's enough space on it for the request). It's defined
* as an int, because it's updated without an exclusive lock. uint16
* would be more appropriate, but int is more likely to be atomically
* fetchable/storable.
*/
int fp_next_slot;
/*
* fp_nodes contains the binary tree, stored in array. The first
* NonLeafNodesPerPage elements are upper nodes, and the following
* LeafNodesPerPage elements are leaf nodes. Unused nodes are zero.
*/
uint8 fp_nodes[1];
} FSMPageData;
typedef FSMPageData *FSMPage;
/*
* Number of non-leaf and leaf nodes, and nodes in total, on an FSM page.
* These definitions are internal to fsmpage.c.
*/
#define NodesPerPage (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
offsetof(FSMPageData, fp_nodes))
#define NonLeafNodesPerPage (BLCKSZ / 2 - 1)
#define LeafNodesPerPage (NodesPerPage - NonLeafNodesPerPage)
/*
* Number of FSM "slots" on a FSM page. This is what should be used
* outside fsmpage.c.
*/
#define SlotsPerFSMPage LeafNodesPerPage
/* Prototypes for functions in fsmpage.c */
extern int fsm_search_avail(Buffer buf, uint8 min_cat, bool advancenext,
bool exclusive_lock_held);
extern uint8 fsm_get_avail(Page page, int slot);
extern uint8 fsm_get_max_avail(Page page);
extern bool fsm_set_avail(Page page, int slot, uint8 value);
extern bool fsm_truncate_avail(Page page, int nslots);
extern bool fsm_rebuild_page(Page page);
#endif /* FSM_INTERNALS_H */

View File

@ -0,0 +1,27 @@
/*-------------------------------------------------------------------------
*
* indexfsm.h
* POSTGRES free space map for quickly finding an unused page in index
*
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/indexfsm.h,v 1.1 2008/09/30 10:52:14 heikki Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef INDEXFSM_H_
#define INDEXFSM_H_
#include "utils/rel.h"
extern void InitIndexFreeSpaceMap(Relation rel);
extern BlockNumber GetFreeIndexPage(Relation rel);
extern void RecordFreeIndexPage(Relation rel, BlockNumber page);
extern void RecordUsedIndexPage(Relation rel, BlockNumber page);
extern void IndexFreeSpaceMapTruncate(Relation rel, BlockNumber nblocks);
#endif /* INDEXFSM_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.39 2008/06/19 21:32:56 tgl Exp $
* $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.40 2008/09/30 10:52:14 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,6 +35,10 @@
* by allowing values not listed in the enum declaration to be assigned.
* The extra value MaxDynamicLWLock is there to keep the compiler from
* deciding that the enum can be represented as char or short ...
*
* If you remove a lock, please replace it with a placeholder like was done
* for FreeSpaceMapLock. This retains the lock numbering, which is helpful for
* DTrace and other external debugging scripts.
*/
typedef enum LWLockId
{
@ -45,7 +49,7 @@ typedef enum LWLockId
ProcArrayLock,
SInvalReadLock,
SInvalWriteLock,
FreeSpaceLock,
UnusedLock1, /* FreeSpaceMapLock used to be here */
WALInsertLock,
WALWriteLock,
ControlFileLock,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/relfilenode.h,v 1.16 2008/08/11 11:05:11 heikki Exp $
* $PostgreSQL: pgsql/src/include/storage/relfilenode.h,v 1.17 2008/09/30 10:52:14 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,11 +23,12 @@
typedef enum ForkNumber
{
InvalidForkNumber = -1,
MAIN_FORKNUM = 0
/* NOTE: change NUM_FORKS below when you add new forks */
MAIN_FORKNUM = 0,
FSM_FORKNUM
/* NOTE: change MAX_FORKNUM below when you add new forks */
} ForkNumber;
#define MAX_FORKNUM MAIN_FORKNUM
#define MAX_FORKNUM FSM_FORKNUM
/*
* RelFileNode must provide all that we need to know to physically access

View File

@ -7,7 +7,7 @@
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.42 2008/09/10 18:09:20 alvherre Exp $
* $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.43 2008/09/30 10:52:14 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -49,7 +49,6 @@ enum config_group
CONN_AUTH_SECURITY,
RESOURCES,
RESOURCES_MEM,
RESOURCES_FSM,
RESOURCES_KERNEL,
WAL,
WAL_SETTINGS,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.107 2008/06/19 00:46:06 alvherre Exp $
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.108 2008/09/30 10:52:14 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -195,6 +195,9 @@ typedef struct RelationData
List *rd_indpred; /* index predicate tree, if any */
void *rd_amcache; /* available for use by index AM */
/* Cached last-seen size of the FSM */
BlockNumber rd_fsm_nblocks_cache;
/* use "struct" here to avoid needing to include pgstat.h: */
struct PgStat_TableStatus *pgstat_info; /* statistics collection area */
} RelationData;