From e0c9301c87634f21c0a7c6305bdc6da15d6ba375 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 27 Jun 2001 23:31:40 +0000
Subject: [PATCH] Install infrastructure for shared-memory free space map. 
 Doesn't actually do anything yet, but it has the necessary connections to
 initialization and so forth.  Make some gestures towards allowing number of
 blocks in a relation to be BlockNumber, ie, unsigned int, rather than signed
 int. (I doubt I got all the places that are sloppy about it, yet.)  On the
 way, replace the hardwired NLOCKS_PER_XACT fudge factor with a GUC variable.

---
 doc/src/sgml/runtime.sgml                     |  38 +++-
 src/backend/access/hash/hashpage.c            |   7 +-
 src/backend/access/heap/heapam.c              |  36 ++--
 src/backend/access/heap/hio.c                 |   4 +-
 src/backend/access/nbtree/nbtpage.c           |   7 +-
 src/backend/catalog/heap.c                    |   5 +-
 src/backend/catalog/index.c                   |  13 +-
 src/backend/commands/vacuum.c                 | 116 ++++++-----
 src/backend/storage/Makefile                  |   4 +-
 src/backend/storage/freespace/Makefile        |  30 +++
 src/backend/storage/freespace/freespace.c     | 183 ++++++++++++++++++
 src/backend/storage/ipc/ipci.c                |  16 +-
 src/backend/storage/ipc/spin.c                |  27 ++-
 src/backend/storage/lmgr/lock.c               |  14 +-
 src/backend/storage/smgr/md.c                 |  99 +++++-----
 src/backend/storage/smgr/mm.c                 |  10 +-
 src/backend/storage/smgr/smgr.c               |  43 ++--
 src/backend/utils/cache/relcache.c            |  97 ++--------
 src/backend/utils/misc/guc.c                  |  18 +-
 src/backend/utils/misc/postgresql.conf.sample |  13 +-
 src/include/commands/vacuum.h                 |   7 +-
 src/include/storage/freespace.h               |  53 +++++
 src/include/storage/ipc.h                     |   3 +-
 src/include/storage/lock.h                    |  17 +-
 src/include/storage/smgr.h                    |  15 +-
 src/include/utils/rel.h                       |  13 +-
 26 files changed, 572 insertions(+), 316 deletions(-)
 create mode 100644 src/backend/storage/freespace/Makefile
 create mode 100644 src/backend/storage/freespace/freespace.c
 create mode 100644 src/include/storage/freespace.h

diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 53add130cb1..d39d2d5d76f 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.69 2001/06/23 00:03:10 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.70 2001/06/27 23:31:37 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -1131,6 +1131,42 @@ dynamic_library_path = '/usr/local/lib:/home/my_project/lib:$libdir:$libdir/cont
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term>MAX_FSM_RELATIONS (<type>integer</type>)</term>
+      <listitem>
+       <para>
+        Sets the maximum number of relations (tables) for which free space
+	will be tracked in the shared free-space map.
+	The default is 100.  This option can only be set at server start.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>MAX_FSM_PAGES (<type>integer</type>)</term>
+      <listitem>
+       <para>
+        Sets the maximum number of disk pages for which free space
+	will be tracked in the shared free-space map.
+	The default is 10000.  This option can only be set at server start.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>MAX_LOCKS_PER_XACT (<type>integer</type>)</term>
+      <listitem>
+       <para>
+        The shared lock table is sized on the assumption that at most
+	max_locks_per_xact * max_connections distinct objects will need
+	to be locked at any one time.  The default, 64, has historically
+	proven sufficient, but you might need to raise this value if you
+	have clients that touch many different tables in a single transaction.
+	This option can only be set at server start.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term>PORT (<type>integer</type>)</term>
       <listitem>
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 156db9078a8..d1b3aaa2325 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/hash/hashpage.c,v 1.30 2001/03/07 21:20:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/hash/hashpage.c,v 1.31 2001/06/27 23:31:37 tgl Exp $
  *
  * NOTES
  *	  Postgres hash pages look like ordinary relation pages.  The opaque
@@ -70,18 +70,15 @@ _hash_metapinit(Relation rel)
 	int			nbuckets;
 	uint32		nelem;			/* number elements */
 	uint32		lg2nelem;		/* _hash_log2(nelem)   */
-	uint32		nblocks;
 	uint16		i;
 
 	/* can't be sharing this with anyone, now... */
 	if (USELOCKING)
 		LockRelation(rel, AccessExclusiveLock);
 
-	if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0)
-	{
+	if (RelationGetNumberOfBlocks(rel) != 0)
 		elog(ERROR, "Cannot initialize non-empty hash table %s",
 			 RelationGetRelationName(rel));
-	}
 
 	metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE);
 	pg = BufferGetPage(metabuf);
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6e0974ac32d..b86425f7d11 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.119 2001/06/22 19:16:20 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.120 2001/06/27 23:31:38 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -121,8 +121,8 @@ heapgettup(Relation relation,
 {
 	ItemId		lpp;
 	Page		dp;
-	int			page;
-	int			pages;
+	BlockNumber	page;
+	BlockNumber	pages;
 	int			lines;
 	OffsetNumber lineoff;
 	int			linesleft;
@@ -172,7 +172,7 @@ heapgettup(Relation relation,
 	/*
 	 * return null immediately if relation is empty
 	 */
-	if (!(pages = relation->rd_nblocks))
+	if ((pages = relation->rd_nblocks) == 0)
 	{
 		if (BufferIsValid(*buffer))
 			ReleaseBuffer(*buffer);
@@ -233,15 +233,8 @@ heapgettup(Relation relation,
 		{
 			page = ItemPointerGetBlockNumber(tid);		/* current page */
 		}
-		if (page < 0)
-		{
-			if (BufferIsValid(*buffer))
-				ReleaseBuffer(*buffer);
-			*buffer = InvalidBuffer;
-			tuple->t_datamcxt = NULL;
-			tuple->t_data = NULL;
-			return;
-		}
+
+		Assert(page < pages);
 
 		*buffer = ReleaseAndReadBuffer(*buffer,
 									   relation,
@@ -283,15 +276,7 @@ heapgettup(Relation relation,
 				OffsetNumberNext(ItemPointerGetOffsetNumber(tid));
 		}
 
-		if (page >= pages)
-		{
-			if (BufferIsValid(*buffer))
-				ReleaseBuffer(*buffer);
-			*buffer = InvalidBuffer;
-			tuple->t_datamcxt = NULL;
-			tuple->t_data = NULL;
-			return;
-		}
+		Assert(page < pages);
 
 		*buffer = ReleaseAndReadBuffer(*buffer,
 									   relation,
@@ -369,12 +354,11 @@ heapgettup(Relation relation,
 		 * and it's time to move to the next.
 		 */
 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-		page = (dir < 0) ? (page - 1) : (page + 1);
 
 		/*
 		 * return NULL if we've exhausted all the pages
 		 */
-		if (page < 0 || page >= pages)
+		if ((dir < 0) ? (page == 0) : (page+1 >= pages))
 		{
 			if (BufferIsValid(*buffer))
 				ReleaseBuffer(*buffer);
@@ -384,6 +368,10 @@ heapgettup(Relation relation,
 			return;
 		}
 
+		page = (dir < 0) ? (page - 1) : (page + 1);
+
+		Assert(page < pages);
+
 		*buffer = ReleaseAndReadBuffer(*buffer,
 									   relation,
 									   page,
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index 1451dc2ecc5..3a520f2c315 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Id: hio.c,v 1.39 2001/05/16 22:35:12 tgl Exp $
+ *	  $Id: hio.c,v 1.40 2001/06/27 23:31:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -147,7 +147,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
 	 */
 	relation->rd_nblocks = RelationGetNumberOfBlocks(relation);
 
-	if ((BlockNumber) relation->rd_nblocks > oldnblocks)
+	if (relation->rd_nblocks > oldnblocks)
 	{
 		/*
 		 * Someone else has indeed extended the relation recently.
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 460d6c834c1..67e1407b22b 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtpage.c,v 1.51 2001/03/22 03:59:14 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtpage.c,v 1.52 2001/06/27 23:31:38 tgl Exp $
  *
  *	NOTES
  *	   Postgres btree pages look like ordinary relation pages.	The opaque
@@ -55,7 +55,6 @@ _bt_metapinit(Relation rel)
 {
 	Buffer		buf;
 	Page		pg;
-	int			nblocks;
 	BTMetaPageData metad;
 	BTPageOpaque op;
 
@@ -63,11 +62,9 @@ _bt_metapinit(Relation rel)
 	if (USELOCKING)
 		LockRelation(rel, AccessExclusiveLock);
 
-	if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0)
-	{
+	if (RelationGetNumberOfBlocks(rel) != 0)
 		elog(ERROR, "Cannot initialize non-empty btree %s",
 			 RelationGetRelationName(rel));
-	}
 
 	buf = ReadBuffer(rel, P_NEW);
 	pg = BufferGetPage(buf);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a7f22cd6cfe..1f2cdf9131d 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.168 2001/06/18 16:13:21 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.169 2001/06/27 23:31:38 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1089,6 +1089,7 @@ RelationTruncateIndexes(Oid heapId)
 		/* Now truncate the actual data and set blocks to zero */
 		smgrtruncate(DEFAULT_SMGR, currentIndex, 0);
 		currentIndex->rd_nblocks = 0;
+		currentIndex->rd_targblock = InvalidBlockNumber;
 
 		/* Initialize the index and rebuild */
 		InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
@@ -1143,9 +1144,9 @@ heap_truncate(char *relname)
 	DropRelationBuffers(rel);
 
 	/* Now truncate the actual data and set blocks to zero */
-
 	smgrtruncate(DEFAULT_SMGR, rel, 0);
 	rel->rd_nblocks = 0;
+	rel->rd_targblock = InvalidBlockNumber;
 
 	/* If this relation has indexes, truncate the indexes too */
 	RelationTruncateIndexes(rid);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 8046e422967..34989055b62 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.154 2001/06/12 05:55:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.155 2001/06/27 23:31:38 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1456,7 +1456,7 @@ UpdateStats(Oid relid, double reltuples)
 	Relation	pg_class;
 	HeapTuple	tuple;
 	HeapTuple	newtup;
-	long		relpages;
+	BlockNumber	relpages;
 	int			i;
 	Form_pg_class rd_rel;
 	Relation	idescs[Num_pg_class_indices];
@@ -1558,7 +1558,7 @@ UpdateStats(Oid relid, double reltuples)
 			reltuples = 1000;
 		}
 		else
-			reltuples = relpages * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts);
+			reltuples = (double) relpages * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts);
 	}
 
 	/*
@@ -1566,7 +1566,7 @@ UpdateStats(Oid relid, double reltuples)
 	 * place with the new values so that the cache contains the latest
 	 * copy.
 	 */
-	whichRel->rd_rel->relpages = relpages;
+	whichRel->rd_rel->relpages = (int32) relpages;
 	whichRel->rd_rel->reltuples = reltuples;
 
 	/*
@@ -1581,7 +1581,7 @@ UpdateStats(Oid relid, double reltuples)
 		 */
 		rd_rel = (Form_pg_class) GETSTRUCT(tuple);
 		LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
-		rd_rel->relpages = relpages;
+		rd_rel->relpages = (int32) relpages;
 		rd_rel->reltuples = reltuples;
 		LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
 		WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
@@ -1600,7 +1600,7 @@ UpdateStats(Oid relid, double reltuples)
 		}
 
 		replace[Anum_pg_class_relpages - 1] = 'r';
-		values[Anum_pg_class_relpages - 1] = Int32GetDatum(relpages);
+		values[Anum_pg_class_relpages - 1] = Int32GetDatum((int32) relpages);
 		replace[Anum_pg_class_reltuples - 1] = 'r';
 		values[Anum_pg_class_reltuples - 1] = Float4GetDatum((float4) reltuples);
 		newtup = heap_modifytuple(tuple, pg_class, values, nulls, replace);
@@ -1962,6 +1962,7 @@ reindex_index(Oid indexId, bool force, bool inplace)
 		/* Now truncate the actual data and set blocks to zero */
 		smgrtruncate(DEFAULT_SMGR, iRel, 0);
 		iRel->rd_nblocks = 0;
+		iRel->rd_targblock = InvalidBlockNumber;
 	}
 
 	/* Initialize the index and rebuild */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index c77b2fe8b90..9c66842feb6 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.197 2001/06/22 19:16:21 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.198 2001/06/27 23:31:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,8 +107,8 @@ typedef VTupleMoveData *VTupleMove;
 typedef struct VRelStats
 {
 	Oid			relid;
-	long		num_pages;
-	long		num_tuples;
+	BlockNumber	rel_pages;
+	double		rel_tuples;
 	Size		min_tlen;
 	Size		max_tlen;
 	bool		hasindex;
@@ -143,8 +143,8 @@ static void vacuum_heap(VRelStats *vacrelstats, Relation onerel,
 						VacPageList vacpagelist);
 static void vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage);
 static void vacuum_index(VacPageList vacpagelist, Relation indrel,
-						 long num_tuples, int keep_tuples);
-static void scan_index(Relation indrel, long num_tuples);
+						 double num_tuples, int keep_tuples);
+static void scan_index(Relation indrel, double num_tuples);
 static VacPage tid_reaped(ItemPointer itemptr, VacPageList vacpagelist);
 static void reap_page(VacPageList vacpagelist, VacPage vacpage);
 static void vpage_insert(VacPageList vacpagelist, VacPage vpnew);
@@ -487,8 +487,8 @@ vacuum_rel(Oid relid)
 	 */
 	vacrelstats = (VRelStats *) palloc(sizeof(VRelStats));
 	vacrelstats->relid = relid;
-	vacrelstats->num_pages = 0;
-	vacrelstats->num_tuples = 0;
+	vacrelstats->rel_pages = 0;
+	vacrelstats->rel_tuples = 0;
 	vacrelstats->hasindex = false;
 
 	GetXmaxRecent(&XmaxRecent);
@@ -535,13 +535,13 @@ vacuum_rel(Oid relid)
 		{
 			for (i = 0; i < nindices; i++)
 				vacuum_index(&vacuum_pages, Irel[i],
-							 vacrelstats->num_tuples, 0);
+							 vacrelstats->rel_tuples, 0);
 		}
 		else
 		{
 			/* just scan indices to update statistic */
 			for (i = 0; i < nindices; i++)
-				scan_index(Irel[i], vacrelstats->num_tuples);
+				scan_index(Irel[i], vacrelstats->rel_tuples);
 		}
 	}
 
@@ -562,14 +562,13 @@ vacuum_rel(Oid relid)
 		}
 		else
 		{
-
 			/*
 			 * Flush dirty pages out to disk.  We must do this even if we
 			 * didn't do anything else, because we want to ensure that all
 			 * tuples have correct on-row commit status on disk (see
 			 * bufmgr.c's comments for FlushRelationBuffers()).
 			 */
-			i = FlushRelationBuffers(onerel, vacrelstats->num_pages);
+			i = FlushRelationBuffers(onerel, vacrelstats->rel_pages);
 			if (i < 0)
 				elog(ERROR, "VACUUM (vacuum_rel): FlushRelationBuffers returned %d",
 					 i);
@@ -584,8 +583,8 @@ vacuum_rel(Oid relid)
 	heap_close(onerel, NoLock);
 
 	/* update statistics in pg_class */
-	vac_update_relstats(vacrelstats->relid, vacrelstats->num_pages,
-						vacrelstats->num_tuples, vacrelstats->hasindex);
+	vac_update_relstats(vacrelstats->relid, vacrelstats->rel_pages,
+						vacrelstats->rel_tuples, vacrelstats->hasindex);
 
 	/*
 	 * Complete the transaction and free all temporary memory used.
@@ -637,7 +636,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 	char	   *relname;
 	VacPage		vacpage,
 				vp;
-	long		num_tuples;
+	double		num_tuples;
 	uint32		tups_vacuumed,
 				nkeep,
 				nunused,
@@ -662,8 +661,9 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 	relname = RelationGetRelationName(onerel);
 	elog(MESSAGE_LEVEL, "--Relation %s--", relname);
 
-	tups_vacuumed = num_tuples = nkeep = nunused = ncrash = empty_pages =
+	tups_vacuumed = nkeep = nunused = ncrash = empty_pages =
 		new_pages = changed_pages = empty_end_pages = 0;
+	num_tuples = 0;
 	free_size = usable_free_size = 0;
 
 	nblocks = RelationGetNumberOfBlocks(onerel);
@@ -922,7 +922,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 			}
 			else
 			{
-				num_tuples++;
+				num_tuples += 1;
 				notup = false;
 				if (tuple.t_len < min_tlen)
 					min_tlen = tuple.t_len;
@@ -966,8 +966,8 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 	pfree(vacpage);
 
 	/* save stats in the rel list for use later */
-	vacrelstats->num_tuples = num_tuples;
-	vacrelstats->num_pages = nblocks;
+	vacrelstats->rel_tuples = num_tuples;
+	vacrelstats->rel_pages = nblocks;
 	if (num_tuples == 0)
 		min_tlen = max_tlen = 0;
 	vacrelstats->min_tlen = min_tlen;
@@ -1014,7 +1014,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 	}
 
 	elog(MESSAGE_LEVEL, "Pages %u: Changed %u, reaped %u, Empty %u, New %u; \
-Tup %lu: Vac %u, Keep/VTL %u/%u, Crash %u, UnUsed %u, MinLen %lu, MaxLen %lu; \
+Tup %.0f: Vac %u, Keep/VTL %u/%u, Crash %u, UnUsed %u, MinLen %lu, MaxLen %lu; \
 Re-using: Free/Avail. Space %lu/%lu; EndEmpty/Avail. Pages %u/%u. %s",
 		 nblocks, changed_pages, vacuum_pages->num_pages, empty_pages,
 		 new_pages, num_tuples, tups_vacuumed,
@@ -1048,6 +1048,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 				cur_buffer;
 	int			nblocks,
 				blkno;
+	BlockNumber	last_move_dest_block = 0,
+				last_vacuum_block;
 	Page		page,
 				ToPage = NULL;
 	OffsetNumber offnum,
@@ -1069,9 +1071,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 				vacpage,
 			   *curpage;
 	int			cur_item = 0;
-	int			last_move_dest_block = -1,
-				last_vacuum_block,
-				i = 0;
+	int			i;
 	Size		tuple_len;
 	int			num_moved,
 				num_fraged_pages,
@@ -1117,7 +1117,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 	 * NB: this code depends on the vacuum_pages and fraged_pages lists being
 	 * in order, and on fraged_pages being a subset of vacuum_pages.
 	 */
-	nblocks = vacrelstats->num_pages;
+	nblocks = vacrelstats->rel_pages;
 	for (blkno = nblocks - vacuum_pages->empty_end_pages - 1;
 		 blkno > last_move_dest_block;
 		 blkno--)
@@ -1152,11 +1152,10 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 			else
 			{
 				last_vacuum_page = NULL;
-				last_vacuum_block = -1;
+				last_vacuum_block = InvalidBlockNumber;
 			}
 			if (num_fraged_pages > 0 &&
-				fraged_pages->pagedesc[num_fraged_pages - 1]->blkno ==
-				(BlockNumber) blkno)
+				fraged_pages->pagedesc[num_fraged_pages - 1]->blkno == blkno)
 			{
 				/* page is in fraged_pages too; remove it */
 				--num_fraged_pages;
@@ -1577,7 +1576,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 					}
 					END_CRIT_SECTION();
 
-					if (((int) destvacpage->blkno) > last_move_dest_block)
+					if (destvacpage->blkno > last_move_dest_block)
 						last_move_dest_block = destvacpage->blkno;
 
 					/*
@@ -1710,9 +1709,9 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 								 InvalidOffsetNumber, LP_USED);
 			if (newoff == InvalidOffsetNumber)
 			{
-				elog(STOP, "\
-failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)",
-					 (unsigned long) tuple_len, cur_page->blkno, (unsigned long) cur_page->free,
+				elog(STOP, "failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)",
+					 (unsigned long) tuple_len,
+					 cur_page->blkno, (unsigned long) cur_page->free,
 					 cur_page->offsets_used, cur_page->offsets_free);
 			}
 			newitemid = PageGetItemId(ToPage, newoff);
@@ -1746,7 +1745,7 @@ failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)"
 			cur_page->offsets_used++;
 			num_moved++;
 			cur_page->free = ((PageHeader) ToPage)->pd_upper - ((PageHeader) ToPage)->pd_lower;
-			if (((int) cur_page->blkno) > last_move_dest_block)
+			if (cur_page->blkno > last_move_dest_block)
 				last_move_dest_block = cur_page->blkno;
 
 			vacpage->offsets[vacpage->offsets_free++] = offnum;
@@ -1882,7 +1881,7 @@ failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)"
 	checked_moved = 0;
 	for (i = 0, curpage = vacuum_pages->pagedesc; i < vacuumed_pages; i++, curpage++)
 	{
-		Assert((*curpage)->blkno < (BlockNumber) blkno);
+		Assert((*curpage)->blkno < blkno);
 		buf = ReadBuffer(onerel, (*curpage)->blkno);
 		LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 		page = BufferGetPage(buf);
@@ -1959,11 +1958,11 @@ failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)"
 			Assert(keep_tuples >= 0);
 			for (i = 0; i < nindices; i++)
 				vacuum_index(&Nvacpagelist, Irel[i],
-							 vacrelstats->num_tuples, keep_tuples);
+							 vacrelstats->rel_tuples, keep_tuples);
 		}
 
 		/* clean moved tuples from last page in Nvacpagelist list */
-		if (vacpage->blkno == (BlockNumber) (blkno - 1) &&
+		if (vacpage->blkno == (blkno - 1) &&
 			vacpage->offsets_free > 0)
 		{
 			OffsetNumber unbuf[BLCKSZ/sizeof(OffsetNumber)];
@@ -2037,8 +2036,7 @@ failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)"
 	if (blkno < nblocks)
 	{
 		blkno = smgrtruncate(DEFAULT_SMGR, onerel, blkno);
-		Assert(blkno >= 0);
-		vacrelstats->num_pages = blkno; /* set new number of blocks */
+		vacrelstats->rel_pages = blkno; /* set new number of blocks */
 	}
 
 	if (Irel != (Relation *) NULL)		/* pfree index' allocations */
@@ -2063,7 +2061,8 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages)
 {
 	Buffer		buf;
 	VacPage    *vacpage;
-	long		nblocks;
+	BlockNumber	relblocks;
+	int			nblocks;
 	int			i;
 
 	nblocks = vacuum_pages->num_pages;
@@ -2087,10 +2086,10 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages)
 	 * tuples have correct on-row commit status on disk (see bufmgr.c's
 	 * comments for FlushRelationBuffers()).
 	 */
-	Assert(vacrelstats->num_pages >= vacuum_pages->empty_end_pages);
-	nblocks = vacrelstats->num_pages - vacuum_pages->empty_end_pages;
+	Assert(vacrelstats->rel_pages >= (BlockNumber) vacuum_pages->empty_end_pages);
+	relblocks = vacrelstats->rel_pages - vacuum_pages->empty_end_pages;
 
-	i = FlushRelationBuffers(onerel, nblocks);
+	i = FlushRelationBuffers(onerel, relblocks);
 	if (i < 0)
 		elog(ERROR, "VACUUM (vacuum_heap): FlushRelationBuffers returned %d",
 			 i);
@@ -2098,12 +2097,11 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages)
 	/* truncate relation if there are some empty end-pages */
 	if (vacuum_pages->empty_end_pages > 0)
 	{
-		elog(MESSAGE_LEVEL, "Rel %s: Pages: %lu --> %lu.",
+		elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u.",
 			 RelationGetRelationName(onerel),
-			 vacrelstats->num_pages, nblocks);
-		nblocks = smgrtruncate(DEFAULT_SMGR, onerel, nblocks);
-		Assert(nblocks >= 0);
-		vacrelstats->num_pages = nblocks;		/* set new number of
+			 vacrelstats->rel_pages, relblocks);
+		relblocks = smgrtruncate(DEFAULT_SMGR, onerel, relblocks);
+		vacrelstats->rel_pages = relblocks;		/* set new number of
 												 * blocks */
 	}
 }
@@ -2148,12 +2146,12 @@ vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage)
  *
  */
 static void
-scan_index(Relation indrel, long num_tuples)
+scan_index(Relation indrel, double num_tuples)
 {
 	RetrieveIndexResult res;
 	IndexScanDesc iscan;
-	long		nitups;
-	int			nipages;
+	BlockNumber	nipages;
+	double		nitups;
 	VacRUsage	ru0;
 
 	init_rusage(&ru0);
@@ -2165,7 +2163,7 @@ scan_index(Relation indrel, long num_tuples)
 	while ((res = index_getnext(iscan, ForwardScanDirection))
 		   != (RetrieveIndexResult) NULL)
 	{
-		nitups++;
+		nitups += 1;
 		pfree(res);
 	}
 
@@ -2175,12 +2173,12 @@ scan_index(Relation indrel, long num_tuples)
 	nipages = RelationGetNumberOfBlocks(indrel);
 	vac_update_relstats(RelationGetRelid(indrel), nipages, nitups, false);
 
-	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %lu. %s",
+	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f. %s",
 		 RelationGetRelationName(indrel), nipages, nitups,
 		 show_rusage(&ru0));
 
 	if (nitups != num_tuples)
-		elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%lu) IS NOT THE SAME AS HEAP' (%lu).\
+		elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f).\
 \n\tRecreate the index.",
 			 RelationGetRelationName(indrel), nitups, num_tuples);
 
@@ -2200,14 +2198,14 @@ scan_index(Relation indrel, long num_tuples)
  */
 static void
 vacuum_index(VacPageList vacpagelist, Relation indrel,
-			 long num_tuples, int keep_tuples)
+			 double num_tuples, int keep_tuples)
 {
 	RetrieveIndexResult res;
 	IndexScanDesc iscan;
 	ItemPointer heapptr;
 	int			tups_vacuumed;
-	long		num_index_tuples;
-	int			num_pages;
+	BlockNumber	num_pages;
+	double		num_index_tuples;
 	VacPage		vp;
 	VacRUsage	ru0;
 
@@ -2242,7 +2240,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
 			index_delete(indrel, &res->index_iptr);
 		}
 		else
-			num_index_tuples++;
+			num_index_tuples += 1;
 
 		pfree(res);
 	}
@@ -2254,13 +2252,13 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
 	vac_update_relstats(RelationGetRelid(indrel),
 						num_pages, num_index_tuples, false);
 
-	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %lu: Deleted %u. %s",
+	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f: Deleted %u. %s",
 		 RelationGetRelationName(indrel), num_pages,
 		 num_index_tuples - keep_tuples, tups_vacuumed,
 		 show_rusage(&ru0));
 
 	if (num_index_tuples != num_tuples + keep_tuples)
-		elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%lu) IS NOT THE SAME AS HEAP' (%lu).\
+		elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f).\
 \n\tRecreate the index.",
 		  RelationGetRelationName(indrel), num_index_tuples, num_tuples);
 
@@ -2333,7 +2331,7 @@ tid_reaped(ItemPointer itemptr, VacPageList vacpagelist)
  *		these are.
  */
 void
-vac_update_relstats(Oid relid, long num_pages, double num_tuples,
+vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
 					bool hasindex)
 {
 	Relation	rd;
@@ -2361,8 +2359,8 @@ vac_update_relstats(Oid relid, long num_pages, double num_tuples,
 
 	/* overwrite the existing statistics in the tuple */
 	pgcform = (Form_pg_class) GETSTRUCT(&rtup);
+	pgcform->relpages = (int32) num_pages;
 	pgcform->reltuples = num_tuples;
-	pgcform->relpages = num_pages;
 	pgcform->relhasindex = hasindex;
 
 	/* invalidate the tuple in the cache and write the buffer */
diff --git a/src/backend/storage/Makefile b/src/backend/storage/Makefile
index d9f65449110..ace37e6ac66 100644
--- a/src/backend/storage/Makefile
+++ b/src/backend/storage/Makefile
@@ -1,14 +1,14 @@
 #
 # Makefile for the storage manager subsystem
 #
-# $Header: /cvsroot/pgsql/src/backend/storage/Makefile,v 1.8 2000/08/31 16:10:30 petere Exp $
+# $Header: /cvsroot/pgsql/src/backend/storage/Makefile,v 1.9 2001/06/27 23:31:39 tgl Exp $
 #
 
 subdir = src/backend/storage
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS     := buffer file ipc large_object lmgr page smgr
+SUBDIRS     := buffer file freespace ipc large_object lmgr page smgr
 SUBDIROBJS  := $(SUBDIRS:%=%/SUBSYS.o)
 
 all: SUBSYS.o
diff --git a/src/backend/storage/freespace/Makefile b/src/backend/storage/freespace/Makefile
new file mode 100644
index 00000000000..9d4c5c5793b
--- /dev/null
+++ b/src/backend/storage/freespace/Makefile
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for storage/freespace
+#
+# IDENTIFICATION
+#    $Header: /cvsroot/pgsql/src/backend/storage/freespace/Makefile,v 1.1 2001/06/27 23:31:39 tgl Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/storage/freespace
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = freespace.o
+
+all: SUBSYS.o
+
+SUBSYS.o: $(OBJS)
+	$(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS)
+
+depend dep:
+	$(CC) -MM $(CFLAGS) *.c >depend
+
+clean: 
+	rm -f SUBSYS.o $(OBJS)
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
new file mode 100644
index 00000000000..84f7066348e
--- /dev/null
+++ b/src/backend/storage/freespace/freespace.c
@@ -0,0 +1,183 @@
+/*-------------------------------------------------------------------------
+ *
+ * freespace.c
+ *	  POSTGRES free space map for quickly finding free space in relations
+ *
+ *
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/freespace/freespace.c,v 1.1 2001/06/27 23:31:39 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/freespace.h"
+#include "storage/itemid.h"
+#include "storage/shmem.h"
+
+
+/*
+ * Shared free-space-map objects
+ *
+ * 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;
+typedef struct FSMChunk FSMChunk;
+
+/* Header for whole map */
+struct FSMHeader
+{
+	HTAB	   *relationHash;	/* hashtable of FSMRelation entries */
+	FSMRelation *relationList;	/* FSMRelations in order by recency of use */
+	int			numRelations;	/* number of FSMRelations now in use */
+	FSMChunk   *freeChunks;		/* linked list of currently-free chunks */
+};
+
+/*
+ * 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 *nextRel;		/* next rel in order by recency of use */
+	FSMRelation *priorRel;		/* prior rel in order by recency of use */
+	FSMChunk   *relChunks;		/* linked list of page info chunks */
+};
+
+#define SHMEM_FSMHASH_KEYSIZE  sizeof(RelFileNode)
+#define SHMEM_FSMHASH_DATASIZE (sizeof(FSMRelation) - SHMEM_FSMHASH_KEYSIZE)
+
+#define CHUNKPAGES  32			/* each chunk can store this many pages */
+
+struct FSMChunk
+{
+	FSMChunk   *next;			/* linked-list link */
+	int			numPages;		/* number of pages described here */
+	BlockNumber	pages[CHUNKPAGES]; /* page numbers within relation */
+	ItemLength	bytes[CHUNKPAGES]; /* free space available on each page */
+};
+
+
+SPINLOCK	FreeSpaceLock;		/* in Shmem or created in
+								 * CreateSpinlocks() */
+
+int		MaxFSMRelations;		/* these are set by guc.c */
+int		MaxFSMPages;
+
+static FSMHeader *FreeSpaceMap;	/* points to FSMHeader in shared memory */
+
+
+/*
+ * InitFreeSpaceMap -- Initialize the freespace module.
+ *
+ * This must be called once during shared memory initialization.
+ * It builds the empty free space map table.  FreeSpaceLock must also be
+ * initialized at some point, but is not touched here --- we assume there is
+ * no need for locking, since only the calling process can be accessing shared
+ * memory as yet.  FreeSpaceShmemSize() was called previously while computing
+ * the space needed for shared memory.
+ */
+void
+InitFreeSpaceMap(void)
+{
+	HASHCTL		info;
+	FSMChunk   *chunks,
+			   *prevchunk;
+	int			nchunks;
+
+	/* Create table header */
+	FreeSpaceMap = (FSMHeader *) ShmemAlloc(sizeof(FSMHeader));
+	if (FreeSpaceMap == NULL)
+		elog(FATAL, "Insufficient shared memory for free space map");
+	MemSet(FreeSpaceMap, 0, sizeof(FSMHeader));
+
+	/* Create hashtable for FSMRelations */
+	info.keysize = SHMEM_FSMHASH_KEYSIZE;
+	info.datasize = SHMEM_FSMHASH_DATASIZE;
+	info.hash = tag_hash;
+
+	FreeSpaceMap->relationHash = ShmemInitHash("Free Space Map Hash",
+											   MaxFSMRelations / 10,
+											   MaxFSMRelations,
+											   &info,
+											   (HASH_ELEM | HASH_FUNCTION));
+
+	if (!FreeSpaceMap->relationHash)
+		elog(FATAL, "Insufficient shared memory for free space map");
+
+	/* Allocate FSMChunks and fill up the free-chunks list */
+	nchunks = (MaxFSMPages - 1) / CHUNKPAGES + 1;
+
+	chunks = (FSMChunk *) ShmemAlloc(nchunks * sizeof(FSMChunk));
+	if (chunks == NULL)
+		elog(FATAL, "Insufficient shared memory for free space map");
+
+	prevchunk = NULL;
+	while (nchunks-- > 0)
+	{
+		chunks->next = prevchunk;
+		prevchunk = chunks;
+		chunks++;
+	}
+	FreeSpaceMap->freeChunks = prevchunk;
+}
+
+
+int
+FreeSpaceShmemSize(void)
+{
+	int			size;
+	int			nchunks;
+
+	/*
+	 * There is no point in allowing less than one "chunk" per relation,
+	 * so force MaxFSMPages to be at least CHUNKPAGES * MaxFSMRelations.
+	 */
+	Assert(MaxFSMRelations > 0);
+	if (MaxFSMPages < CHUNKPAGES * MaxFSMRelations)
+		MaxFSMPages = CHUNKPAGES * MaxFSMRelations;
+
+	/* table header */
+	size = MAXALIGN(sizeof(FSMHeader));
+
+	/* hash table, including the FSMRelation objects */
+	size += hash_estimate_size(MaxFSMRelations,
+							   SHMEM_FSMHASH_KEYSIZE,
+							   SHMEM_FSMHASH_DATASIZE);
+
+	/* FSMChunk objects */
+	nchunks = (MaxFSMPages - 1) / CHUNKPAGES + 1;
+
+	size += MAXALIGN(nchunks * sizeof(FSMChunk));
+
+	return size;
+}
+
+
+void
+FreeSpaceMapForgetRel(RelFileNode *rel)
+{
+}
+
+
+#ifdef FREESPACE_DEBUG
+/*
+ * Dump contents of freespace map for debugging.
+ *
+ * We assume caller holds the FreeSpaceLock, or is otherwise unconcerned
+ * about other processes.
+ */
+void
+DumpFreeSpace(void)
+{
+}
+
+#endif	 /* FREESPACE_DEBUG */
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index ed42e51a925..75736c8f240 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.40 2001/03/22 03:59:45 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.41 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
 #include "miscadmin.h"
 #include "access/xlog.h"
 #include "storage/bufmgr.h"
+#include "storage/freespace.h"
 #include "storage/lmgr.h"
 #include "storage/proc.h"
 #include "storage/sinval.h"
@@ -47,8 +48,12 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int maxBackends)
 	 * moderately-accurate estimates for the big hogs, plus 100K for the
 	 * stuff that's too small to bother with estimating.
 	 */
-	size = BufferShmemSize() + LockShmemSize(maxBackends) +
-		XLOGShmemSize() + SLockShmemSize() + SInvalShmemSize(maxBackends);
+	size = BufferShmemSize();
+	size += LockShmemSize(maxBackends);
+	size += XLOGShmemSize();
+	size += SLockShmemSize();
+	size += SInvalShmemSize(maxBackends);
+	size += FreeSpaceShmemSize();
 #ifdef STABLE_MEMORY_STORAGE
 	size += MMShmemSize();
 #endif
@@ -96,4 +101,9 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int maxBackends)
 	 * Set up shared-inval messaging
 	 */
 	CreateSharedInvalidationState(maxBackends);
+
+	/*
+	 * Set up free-space map
+	 */
+	InitFreeSpaceMap();
 }
diff --git a/src/backend/storage/ipc/spin.c b/src/backend/storage/ipc/spin.c
index 33308f0cc1f..05bf5acbbb3 100644
--- a/src/backend/storage/ipc/spin.c
+++ b/src/backend/storage/ipc/spin.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.32 2001/03/22 03:59:45 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.33 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,19 +31,18 @@
 
 
 /* Probably should move these to an appropriate header file */
-extern SPINLOCK ShmemLock;
-extern SPINLOCK ShmemIndexLock;
 extern SPINLOCK BufMgrLock;
-extern SPINLOCK LockMgrLock;
-extern SPINLOCK ProcStructLock;
-extern SPINLOCK SInvalLock;
 extern SPINLOCK OidGenLockId;
 extern SPINLOCK XidGenLockId;
 extern SPINLOCK ControlFileLockId;
-
+extern SPINLOCK ShmemLock;
+extern SPINLOCK ShmemIndexLock;
+extern SPINLOCK LockMgrLock;
+extern SPINLOCK SInvalLock;
+extern SPINLOCK ProcStructLock;
+extern SPINLOCK FreeSpaceLock;
 #ifdef STABLE_MEMORY_STORAGE
 extern SPINLOCK MMCacheLock;
-
 #endif
 
 
@@ -57,16 +56,16 @@ extern SPINLOCK MMCacheLock;
 static void
 InitSpinLockIDs(void)
 {
-	ShmemLock = (SPINLOCK) SHMEMLOCKID;
-	ShmemIndexLock = (SPINLOCK) SHMEMINDEXLOCKID;
 	BufMgrLock = (SPINLOCK) BUFMGRLOCKID;
-	LockMgrLock = (SPINLOCK) LOCKMGRLOCKID;
-	ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
-	SInvalLock = (SPINLOCK) SINVALLOCKID;
 	OidGenLockId = (SPINLOCK) OIDGENLOCKID;
 	XidGenLockId = (SPINLOCK) XIDGENLOCKID;
 	ControlFileLockId = (SPINLOCK) CNTLFILELOCKID;
-
+	ShmemLock = (SPINLOCK) SHMEMLOCKID;
+	ShmemIndexLock = (SPINLOCK) SHMEMINDEXLOCKID;
+	LockMgrLock = (SPINLOCK) LOCKMGRLOCKID;
+	SInvalLock = (SPINLOCK) SINVALLOCKID;
+	ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
+	FreeSpaceLock = (SPINLOCK) FREESPACELOCKID;
 #ifdef STABLE_MEMORY_STORAGE
 	MMCacheLock = (SPINLOCK) MMCACHELOCKID;
 #endif
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 577b420797e..3eb01048274 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.89 2001/06/22 00:04:59 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.90 2001/06/27 23:31:39 tgl Exp $
  *
  * NOTES
  *	  Outside modules can create a lock table and acquire/release
@@ -40,6 +40,13 @@
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
 
+
+/* This configuration variable is used to set the lock table size */
+int		max_locks_per_xact;		/* set by guc.c */
+
+#define NLOCKENTS(maxBackends)	(max_locks_per_xact * (maxBackends))
+
+
 static int WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode,
 		   LOCK *lock, HOLDER *holder);
 static void LockCountMyLocks(SHMEM_OFFSET lockOffset, PROC *proc,
@@ -1388,6 +1395,7 @@ int
 LockShmemSize(int maxBackends)
 {
 	int			size = 0;
+	long		max_table_size = NLOCKENTS(maxBackends);
 
 	size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
 	size += maxBackends * MAXALIGN(sizeof(PROC));		/* each MyProc */
@@ -1395,12 +1403,12 @@ LockShmemSize(int maxBackends)
 																 * lockMethodTable->ctl */
 
 	/* lockHash table */
-	size += hash_estimate_size(NLOCKENTS(maxBackends),
+	size += hash_estimate_size(max_table_size,
 							   SHMEM_LOCKTAB_KEYSIZE,
 							   SHMEM_LOCKTAB_DATASIZE);
 
 	/* holderHash table */
-	size += hash_estimate_size(NLOCKENTS(maxBackends),
+	size += hash_estimate_size(max_table_size,
 							   SHMEM_HOLDERTAB_KEYSIZE,
 							   SHMEM_HOLDERTAB_DATASIZE);
 
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 762aceda73c..54054ee137e 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.85 2001/06/06 17:07:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.86 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,10 +72,10 @@ static MemoryContext MdCxt;		/* context for all my allocations */
 /* routines declared here */
 static void mdclose_fd(int fd);
 static int	_mdfd_getrelnfd(Relation reln);
-static MdfdVec *_mdfd_openseg(Relation reln, int segno, int oflags);
-static MdfdVec *_mdfd_getseg(Relation reln, int blkno);
+static MdfdVec *_mdfd_openseg(Relation reln, BlockNumber segno, int oflags);
+static MdfdVec *_mdfd_getseg(Relation reln, BlockNumber blkno);
 
-static int	_mdfd_blind_getseg(RelFileNode rnode, int blkno);
+static int	_mdfd_blind_getseg(RelFileNode rnode, BlockNumber blkno);
 
 static int	_fdvec_alloc(void);
 static void _fdvec_free(int);
@@ -93,7 +93,7 @@ static BlockNumber _mdnblocks(File file, Size blcksz);
  *		Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
  */
 int
-mdinit()
+mdinit(void)
 {
 	int			i;
 
@@ -194,11 +194,11 @@ mdunlink(RelFileNode rnode)
 	if (status == SM_SUCCESS)
 	{
 		char	   *segpath = (char *) palloc(strlen(path) + 12);
-		int			segno;
+		BlockNumber	segno;
 
 		for (segno = 1;; segno++)
 		{
-			sprintf(segpath, "%s.%d", path, segno);
+			sprintf(segpath, "%s.%u", path, segno);
 			if (unlink(segpath) < 0)
 			{
 				/* ENOENT is expected after the last segment... */
@@ -246,7 +246,7 @@ mdextend(Relation reln, BlockNumber blocknum, char *buffer)
 	v = _mdfd_getseg(reln, blocknum);
 
 #ifndef LET_OS_MANAGE_FILESIZE
-	seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
+	seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE)));
 #ifdef DIAGNOSTIC
 	if (seekpos >= BLCKSZ * RELSEG_SIZE)
 		elog(FATAL, "seekpos too big!");
@@ -283,7 +283,7 @@ mdextend(Relation reln, BlockNumber blocknum, char *buffer)
 
 #ifndef LET_OS_MANAGE_FILESIZE
 #ifdef DIAGNOSTIC
-	if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > RELSEG_SIZE)
+	if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > ((BlockNumber) RELSEG_SIZE))
 		elog(FATAL, "segment too big!");
 #endif
 #endif
@@ -338,7 +338,7 @@ mdopen(Relation reln)
 	Md_fdvec[vfd].mdfd_chain = (MdfdVec *) NULL;
 
 #ifdef DIAGNOSTIC
-	if (_mdnblocks(fd, BLCKSZ) > RELSEG_SIZE)
+	if (_mdnblocks(fd, BLCKSZ) > ((BlockNumber) RELSEG_SIZE))
 		elog(FATAL, "segment too big on relopen!");
 #endif
 #endif
@@ -438,7 +438,7 @@ mdread(Relation reln, BlockNumber blocknum, char *buffer)
 	v = _mdfd_getseg(reln, blocknum);
 
 #ifndef LET_OS_MANAGE_FILESIZE
-	seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
+	seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE)));
 
 #ifdef DIAGNOSTIC
 	if (seekpos >= BLCKSZ * RELSEG_SIZE)
@@ -482,7 +482,7 @@ mdwrite(Relation reln, BlockNumber blocknum, char *buffer)
 	v = _mdfd_getseg(reln, blocknum);
 
 #ifndef LET_OS_MANAGE_FILESIZE
-	seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
+	seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE)));
 #ifdef DIAGNOSTIC
 	if (seekpos >= BLCKSZ * RELSEG_SIZE)
 		elog(FATAL, "seekpos too big!");
@@ -516,7 +516,7 @@ mdflush(Relation reln, BlockNumber blocknum, char *buffer)
 	v = _mdfd_getseg(reln, blocknum);
 
 #ifndef LET_OS_MANAGE_FILESIZE
-	seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
+	seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE)));
 #ifdef DIAGNOSTIC
 	if (seekpos >= BLCKSZ * RELSEG_SIZE)
 		elog(FATAL, "seekpos too big!");
@@ -561,7 +561,7 @@ mdblindwrt(RelFileNode rnode,
 		return SM_FAIL;
 
 #ifndef LET_OS_MANAGE_FILESIZE
-	seekpos = (long) (BLCKSZ * (blkno % RELSEG_SIZE));
+	seekpos = (long) (BLCKSZ * (blkno % ((BlockNumber) RELSEG_SIZE)));
 #ifdef DIAGNOSTIC
 	if (seekpos >= BLCKSZ * RELSEG_SIZE)
 		elog(FATAL, "seekpos too big!");
@@ -659,16 +659,14 @@ mdblindmarkdirty(RelFileNode rnode,
  *
  *		Returns # of blocks, elog's on error.
  */
-int
+BlockNumber
 mdnblocks(Relation reln)
 {
 	int			fd;
 	MdfdVec    *v;
-
 #ifndef LET_OS_MANAGE_FILESIZE
-	int			nblocks;
-	int			segno;
-
+	BlockNumber	nblocks;
+	BlockNumber	segno;
 #endif
 
 	fd = _mdfd_getrelnfd(reln);
@@ -679,10 +677,10 @@ mdnblocks(Relation reln)
 	for (;;)
 	{
 		nblocks = _mdnblocks(v->mdfd_vfd, BLCKSZ);
-		if (nblocks > RELSEG_SIZE)
+		if (nblocks > ((BlockNumber) RELSEG_SIZE))
 			elog(FATAL, "segment too big in mdnblocks!");
-		if (nblocks < RELSEG_SIZE)
-			return (segno * RELSEG_SIZE) + nblocks;
+		if (nblocks < ((BlockNumber) RELSEG_SIZE))
+			return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks;
 		/*
 		 * If segment is exactly RELSEG_SIZE, advance to next one.
 		 */
@@ -713,18 +711,16 @@ mdnblocks(Relation reln)
 /*
  *	mdtruncate() -- Truncate relation to specified number of blocks.
  *
- *		Returns # of blocks or -1 on error.
+ *		Returns # of blocks or InvalidBlockNumber on error.
  */
-int
-mdtruncate(Relation reln, int nblocks)
+BlockNumber
+mdtruncate(Relation reln, BlockNumber nblocks)
 {
-	int			curnblk;
 	int			fd;
 	MdfdVec    *v;
-
+	BlockNumber	curnblk;
 #ifndef LET_OS_MANAGE_FILESIZE
-	int			priorblocks;
-
+	BlockNumber	priorblocks;
 #endif
 
 	/*
@@ -732,8 +728,8 @@ mdtruncate(Relation reln, int nblocks)
 	 * that truncate/delete loop will get them all!
 	 */
 	curnblk = mdnblocks(reln);
-	if (nblocks < 0 || nblocks > curnblk)
-		return -1;				/* bogus request */
+	if (nblocks > curnblk)
+		return InvalidBlockNumber; /* bogus request */
 	if (nblocks == curnblk)
 		return nblocks;			/* no work */
 
@@ -748,7 +744,6 @@ mdtruncate(Relation reln, int nblocks)
 
 		if (priorblocks > nblocks)
 		{
-
 			/*
 			 * This segment is no longer wanted at all (and has already
 			 * been unlinked from the mdfd_chain). We truncate the file
@@ -763,27 +758,25 @@ mdtruncate(Relation reln, int nblocks)
 												 * segment */
 			pfree(ov);
 		}
-		else if (priorblocks + RELSEG_SIZE > nblocks)
+		else if (priorblocks + ((BlockNumber) RELSEG_SIZE) > nblocks)
 		{
-
 			/*
 			 * This is the last segment we want to keep. Truncate the file
 			 * to the right length, and clear chain link that points to
 			 * any remaining segments (which we shall zap). NOTE: if
 			 * nblocks is exactly a multiple K of RELSEG_SIZE, we will
 			 * truncate the K+1st segment to 0 length but keep it. This is
-			 * mainly so that the right thing happens if nblocks=0.
+			 * mainly so that the right thing happens if nblocks==0.
 			 */
-			int			lastsegblocks = nblocks - priorblocks;
+			BlockNumber		lastsegblocks = nblocks - priorblocks;
 
 			if (FileTruncate(v->mdfd_vfd, lastsegblocks * BLCKSZ) < 0)
-				return -1;
+				return InvalidBlockNumber;
 			v = v->mdfd_chain;
 			ov->mdfd_chain = (MdfdVec *) NULL;
 		}
 		else
 		{
-
 			/*
 			 * We still need this segment and 0 or more blocks beyond it,
 			 * so nothing to do here.
@@ -794,7 +787,7 @@ mdtruncate(Relation reln, int nblocks)
 	}
 #else
 	if (FileTruncate(v->mdfd_vfd, nblocks * BLCKSZ) < 0)
-		return -1;
+		return InvalidBlockNumber;
 #endif
 
 	return nblocks;
@@ -940,7 +933,7 @@ _fdvec_free(int fdvec)
 }
 
 static MdfdVec *
-_mdfd_openseg(Relation reln, int segno, int oflags)
+_mdfd_openseg(Relation reln, BlockNumber segno, int oflags)
 {
 	MdfdVec    *v;
 	int			fd;
@@ -953,7 +946,7 @@ _mdfd_openseg(Relation reln, int segno, int oflags)
 	if (segno > 0)
 	{
 		fullpath = (char *) palloc(strlen(path) + 12);
-		sprintf(fullpath, "%s.%d", path, segno);
+		sprintf(fullpath, "%s.%u", path, segno);
 		pfree(path);
 	}
 	else
@@ -977,7 +970,7 @@ _mdfd_openseg(Relation reln, int segno, int oflags)
 	v->mdfd_chain = (MdfdVec *) NULL;
 
 #ifdef DIAGNOSTIC
-	if (_mdnblocks(fd, BLCKSZ) > RELSEG_SIZE)
+	if (_mdnblocks(fd, BLCKSZ) > ((BlockNumber) RELSEG_SIZE))
 		elog(FATAL, "segment too big on openseg!");
 #endif
 #endif
@@ -1007,17 +1000,19 @@ _mdfd_getrelnfd(Relation reln)
 /* Find the segment of the relation holding the specified block */
 
 static MdfdVec *
-_mdfd_getseg(Relation reln, int blkno)
+_mdfd_getseg(Relation reln, BlockNumber blkno)
 {
 	MdfdVec    *v;
-	int			segno;
 	int			fd;
-	int			i;
+#ifndef LET_OS_MANAGE_FILESIZE
+	BlockNumber	segno;
+	BlockNumber	i;
+#endif
 
 	fd = _mdfd_getrelnfd(reln);
 
 #ifndef LET_OS_MANAGE_FILESIZE
-	for (v = &Md_fdvec[fd], segno = blkno / RELSEG_SIZE, i = 1;
+	for (v = &Md_fdvec[fd], segno = blkno / ((BlockNumber) RELSEG_SIZE), i = 1;
 		 segno > 0;
 		 i++, segno--)
 	{
@@ -1038,7 +1033,7 @@ _mdfd_getseg(Relation reln, int blkno)
 			v->mdfd_chain = _mdfd_openseg(reln, i, (segno == 1) ? O_CREAT : 0);
 
 			if (v->mdfd_chain == (MdfdVec *) NULL)
-				elog(ERROR, "cannot open segment %d of relation %s (target block %d): %m",
+				elog(ERROR, "cannot open segment %u of relation %s (target block %u): %m",
 					 i, RelationGetRelationName(reln), blkno);
 		}
 		v = v->mdfd_chain;
@@ -1064,26 +1059,24 @@ _mdfd_getseg(Relation reln, int blkno)
  */
 
 static int
-_mdfd_blind_getseg(RelFileNode rnode, int blkno)
+_mdfd_blind_getseg(RelFileNode rnode, BlockNumber blkno)
 {
 	char	   *path;
 	int			fd;
-
 #ifndef LET_OS_MANAGE_FILESIZE
-	int			segno;
-
+	BlockNumber	segno;
 #endif
 
 	path = relpath(rnode);
 
 #ifndef LET_OS_MANAGE_FILESIZE
 	/* append the '.segno', if needed */
-	segno = blkno / RELSEG_SIZE;
+	segno = blkno / ((BlockNumber) RELSEG_SIZE);
 	if (segno > 0)
 	{
 		char	   *segpath = (char *) palloc(strlen(path) + 12);
 
-		sprintf(segpath, "%s.%d", path, segno);
+		sprintf(segpath, "%s.%u", path, segno);
 		pfree(path);
 		path = segpath;
 	}
diff --git a/src/backend/storage/smgr/mm.c b/src/backend/storage/smgr/mm.c
index 547fc8d8385..791c375de0a 100644
--- a/src/backend/storage/smgr/mm.c
+++ b/src/backend/storage/smgr/mm.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.23 2001/05/10 20:38:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.24 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -489,15 +489,15 @@ mmblindwrt(char *dbstr,
 /*
  *	mmnblocks() -- Get the number of blocks stored in a relation.
  *
- *		Returns # of blocks or -1 on error.
+ *		Returns # of blocks or InvalidBlockNumber on error.
  */
-int
+BlockNumber
 mmnblocks(Relation reln)
 {
 	MMRelTag	rtag;
 	MMRelHashEntry *rentry;
 	bool		found;
-	int			nblocks;
+	BlockNumber	nblocks;
 
 	if (reln->rd_rel->relisshared)
 		rtag.mmrt_dbid = (Oid) 0;
@@ -520,7 +520,7 @@ mmnblocks(Relation reln)
 	if (found)
 		nblocks = rentry->mmrhe_nblocks;
 	else
-		nblocks = -1;
+		nblocks = InvalidBlockNumber;
 
 	SpinRelease(MMCacheLock);
 
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 25598e3cd56..56edbec60f2 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -11,13 +11,14 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/smgr.c,v 1.49 2001/05/10 20:38:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/smgr.c,v 1.50 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "storage/bufmgr.h"
+#include "storage/freespace.h"
 #include "storage/smgr.h"
 #include "utils/memutils.h"
 
@@ -44,8 +45,8 @@ typedef struct f_smgr
 								  char *buffer, bool dofsync);
 	int			(*smgr_markdirty) (Relation reln, BlockNumber blkno);
 	int			(*smgr_blindmarkdirty) (RelFileNode, BlockNumber blkno);
-	int			(*smgr_nblocks) (Relation reln);
-	int			(*smgr_truncate) (Relation reln, int nblocks);
+	BlockNumber	(*smgr_nblocks) (Relation reln);
+	BlockNumber	(*smgr_truncate) (Relation reln, BlockNumber nblocks);
 	int			(*smgr_commit) (void);	/* may be NULL */
 	int			(*smgr_abort) (void);	/* may be NULL */
 	int			(*smgr_sync) (void);
@@ -433,16 +434,10 @@ smgrblindmarkdirty(int16 which,
  *		Returns the number of blocks on success, aborts the current
  *		transaction on failure.
  */
-int
+BlockNumber
 smgrnblocks(int16 which, Relation reln)
 {
-	int			nblocks;
-
-	if ((nblocks = (*(smgrsw[which].smgr_nblocks)) (reln)) < 0)
-		elog(ERROR, "cannot count blocks for %s: %m",
-			 RelationGetRelationName(reln));
-
-	return nblocks;
+	return (*(smgrsw[which].smgr_nblocks)) (reln);
 }
 
 /*
@@ -452,16 +447,24 @@ smgrnblocks(int16 which, Relation reln)
  *		Returns the number of blocks on success, aborts the current
  *		transaction on failure.
  */
-int
-smgrtruncate(int16 which, Relation reln, int nblocks)
+BlockNumber
+smgrtruncate(int16 which, Relation reln, BlockNumber nblocks)
 {
-	int			newblks;
+	BlockNumber		newblks;
 
 	newblks = nblocks;
 	if (smgrsw[which].smgr_truncate)
 	{
-		if ((newblks = (*(smgrsw[which].smgr_truncate)) (reln, nblocks)) < 0)
-			elog(ERROR, "cannot truncate %s to %d blocks: %m",
+		/*
+		 * Tell the free space map to forget this relation, so that it
+		 * stops caching info about the deleted blocks.  XXX perhaps
+		 * tell it to forget only info about blocks beyond nblocks?
+		 */
+		FreeSpaceMapForgetRel(&reln->rd_node);
+
+		newblks = (*(smgrsw[which].smgr_truncate)) (reln, nblocks);
+		if (newblks == InvalidBlockNumber)
+			elog(ERROR, "cannot truncate %s to %u blocks: %m",
 				 RelationGetRelationName(reln), nblocks);
 	}
 
@@ -481,7 +484,6 @@ smgrDoPendingDeletes(bool isCommit)
 		pendingDeletes = pending->next;
 		if (pending->atCommit == isCommit)
 		{
-
 			/*
 			 * Get rid of any leftover buffers for the rel (shouldn't be
 			 * any in the commit case, but there can be in the abort
@@ -489,6 +491,13 @@ smgrDoPendingDeletes(bool isCommit)
 			 */
 			DropRelFileNodeBuffers(pending->relnode);
 
+			/*
+			 * 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.
+			 */
+			FreeSpaceMapForgetRel(&pending->relnode);
+
 			/*
 			 * And delete the physical files.
 			 *
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7b90122b9f2..00b66b2575b 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.139 2001/06/22 19:16:23 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.140 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
  * NOTES
  *		The following code contains many undocumented hacks.  Please be
  *		careful....
- *
  */
 #include "postgres.h"
 
@@ -63,7 +62,6 @@
 
 /*
  *		hardcoded tuple descriptors.  see lib/backend/catalog/pg_attribute.h
- *
  */
 static FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
 static FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
@@ -76,7 +74,6 @@ static FormData_pg_attribute Desc_pg_log[Natts_pg_log] = {Schema_pg_log};
  *
  *		Relations are looked up two ways, by name and by id,
  *		thus there are two hash tables for referencing them.
- *
  */
 static HTAB *RelationNameCache;
 static HTAB *RelationIdCache;
@@ -105,7 +102,6 @@ static bool criticalRelcachesBuilt = false;
 /*
  *		RelationBuildDescInfo exists so code can be shared
  *		between RelationIdGetRelation() and RelationNameGetRelation()
- *
  */
 typedef struct RelationBuildDescInfo
 {
@@ -139,7 +135,6 @@ typedef struct relnodecacheent
 
 /*
  *		macros to manipulate name cache and id cache
- *
  */
 #define RelationCacheInsert(RELATION)	\
 do { \
@@ -285,7 +280,6 @@ static List *insert_ordered_oid(List *list, Oid datum);
 /*
  *		RelationIdGetRelation() and RelationNameGetRelation()
  *						support functions
- *
  */
 
 
@@ -298,7 +292,6 @@ static List *insert_ordered_oid(List *list, Oid datum);
  *
  *		NB: the returned tuple has been copied into palloc'd storage
  *		and must eventually be freed with heap_freetuple.
- *
  */
 static HeapTuple
 ScanPgRelation(RelationBuildDescInfo buildinfo)
@@ -327,7 +320,6 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
 
 	/*
 	 * form a scan key
-	 *
 	 */
 	switch (buildinfo.infotype)
 	{
@@ -352,7 +344,6 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
 
 	/*
 	 * open pg_class and fetch a tuple
-	 *
 	 */
 	pg_class_desc = heap_openr(RelationRelationName, AccessShareLock);
 	pg_class_scan = heap_beginscan(pg_class_desc, 0, SnapshotNow, 1, &key);
@@ -360,7 +351,6 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
 
 	/*
 	 * get set to return tuple
-	 *
 	 */
 	if (!HeapTupleIsValid(pg_class_tuple))
 		return_tuple = pg_class_tuple;
@@ -372,7 +362,6 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
 		 * returned here without having the corresponding buffer pinned.
 		 * so when the buffer gets replaced, all hell breaks loose. this
 		 * bug is discovered and killed by wei on 9/27/91.
-		 *
 		 */
 		return_tuple = heap_copytuple(pg_class_tuple);
 	}
@@ -435,7 +424,6 @@ scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
  *		If 'relation' is NULL, allocate a new RelationData object.
  *		If not, reuse the given object (that path is taken only when
  *		we have to rebuild a relcache entry during RelationClearRelation).
- *
  */
 static Relation
 AllocateRelationDesc(Relation relation, Form_pg_class relp)
@@ -448,16 +436,15 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
 
 	/*
 	 * allocate space for new relation descriptor, if needed
-	 *
 	 */
 	if (relation == NULL)
 		relation = (Relation) palloc(sizeof(RelationData));
 
 	/*
 	 * clear all fields of reldesc
-	 *
 	 */
 	MemSet((char *) relation, 0, sizeof(RelationData));
+	relation->rd_targblock = InvalidBlockNumber;
 
 	/* make sure relation is marked as having no open file yet */
 	relation->rd_fd = -1;
@@ -471,7 +458,6 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
 	 * wouldn't know if the value is valid ... bottom line is that relacl
 	 * *cannot* be retrieved from the relcache.  Get it from the syscache
 	 * if you need it.
-	 *
 	 */
 	relationForm = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);
 
@@ -493,7 +479,6 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
  *
  *		Form the relation's tuple descriptor from information in
  *		the pg_attribute, pg_attrdef & pg_relcheck system cataloges.
- *
  */
 static void
 RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
@@ -574,7 +559,6 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 
 	/*
 	 * form a scan key
-	 *
 	 */
 	ScanKeyEntryInitialize(&key, 0,
 						   Anum_pg_attribute_attrelid,
@@ -583,14 +567,12 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 
 	/*
 	 * open pg_attribute and begin a scan
-	 *
 	 */
 	pg_attribute_desc = heap_openr(AttributeRelationName, AccessShareLock);
 	pg_attribute_scan = heap_beginscan(pg_attribute_desc, 0, SnapshotNow, 1, &key);
 
 	/*
 	 * add attribute data to relation->rd_att
-	 *
 	 */
 	need = relation->rd_rel->relnatts;
 
@@ -639,7 +621,6 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 
 	/*
 	 * end the scan and close the attribute relation
-	 *
 	 */
 	heap_endscan(pg_attribute_scan);
 	heap_close(pg_attribute_desc, AccessShareLock);
@@ -648,7 +629,6 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 	 * The attcacheoff values we read from pg_attribute should all be -1
 	 * ("unknown").  Verify this if assert checking is on.	They will be
 	 * computed when and if needed during tuple access.
-	 *
 	 */
 #ifdef USE_ASSERT_CHECKING
 	{
@@ -664,7 +644,6 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 	 * attribute: it must be zero.	This eliminates the need for special
 	 * cases for attnum=1 that used to exist in fastgetattr() and
 	 * index_getattr().
-	 *
 	 */
 	relation->rd_att->attrs[0]->attcacheoff = 0;
 
@@ -758,7 +737,6 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
 	 * The attcacheoff values we read from pg_attribute should all be -1
 	 * ("unknown").  Verify this if assert checking is on.	They will be
 	 * computed when and if needed during tuple access.
-	 *
 	 */
 #ifdef USE_ASSERT_CHECKING
 	for (i = 0; i < relation->rd_rel->relnatts; i++)
@@ -770,7 +748,6 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
 	 * attribute: it must be zero.	This eliminates the need for special
 	 * cases for attnum=1 that used to exist in fastgetattr() and
 	 * index_getattr().
-	 *
 	 */
 	relation->rd_att->attrs[0]->attcacheoff = 0;
 
@@ -791,7 +768,6 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
  * entry, because that keeps the update logic in RelationClearRelation()
  * manageable.	The other subsidiary data structures are simple enough
  * to be easy to free explicitly, anyway.
- *
  */
 static void
 RelationBuildRuleLock(Relation relation)
@@ -814,7 +790,7 @@ RelationBuildRuleLock(Relation relation)
 	 */
 	rulescxt = AllocSetContextCreate(CacheMemoryContext,
 									 RelationGetRelationName(relation),
-									 0, /* minsize */
+									 0,			/* minsize */
 									 1024,		/* initsize */
 									 1024);		/* maxsize */
 	relation->rd_rulescxt = rulescxt;
@@ -822,7 +798,6 @@ RelationBuildRuleLock(Relation relation)
 	/*
 	 * form an array to hold the rewrite rules (the array is extended if
 	 * necessary)
-	 *
 	 */
 	maxlocks = 4;
 	rules = (RewriteRule **)
@@ -831,7 +806,6 @@ RelationBuildRuleLock(Relation relation)
 
 	/*
 	 * form a scan key
-	 *
 	 */
 	ScanKeyEntryInitialize(&key, 0,
 						   Anum_pg_rewrite_ev_class,
@@ -840,7 +814,6 @@ RelationBuildRuleLock(Relation relation)
 
 	/*
 	 * open pg_rewrite and begin a scan
-	 *
 	 */
 	pg_rewrite_desc = heap_openr(RewriteRelationName, AccessShareLock);
 	pg_rewrite_scan = heap_beginscan(pg_rewrite_desc, 0, SnapshotNow, 1, &key);
@@ -908,14 +881,12 @@ RelationBuildRuleLock(Relation relation)
 
 	/*
 	 * end the scan and close the attribute relation
-	 *
 	 */
 	heap_endscan(pg_rewrite_scan);
 	heap_close(pg_rewrite_desc, AccessShareLock);
 
 	/*
 	 * form a RuleLock and insert into relation
-	 *
 	 */
 	rulelock = (RuleLock *) MemoryContextAlloc(rulescxt, sizeof(RuleLock));
 	rulelock->numLocks = numlocks;
@@ -930,7 +901,6 @@ RelationBuildRuleLock(Relation relation)
  *		Determine whether two RuleLocks are equivalent
  *
  *		Probably this should be in the rules code someplace...
- *
  */
 static bool
 equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
@@ -994,9 +964,9 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
  *		fields:
  *
  *	File				   rd_fd;		 open file descriptor
- *	int					   rd_nblocks;	 number of blocks in rel
+ *	BlockNumber			   rd_nblocks;	 number of blocks in rel
  *										 it will be set in ambeginscan()
- *	uint16				   rd_refcnt;	 reference count
+ *	int					   rd_refcnt;	 reference count
  *	Form_pg_am			   rd_am;		 AM tuple
  *	Form_pg_class		   rd_rel;		 RELATION tuple
  *	Oid					   rd_id;		 relation's object id
@@ -1022,20 +992,17 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 
 	/*
 	 * find the tuple in pg_class corresponding to the given relation id
-	 *
 	 */
 	pg_class_tuple = ScanPgRelation(buildinfo);
 
 	/*
 	 * if no such tuple exists, return NULL
-	 *
 	 */
 	if (!HeapTupleIsValid(pg_class_tuple))
 		return NULL;
 
 	/*
 	 * get information from the pg_class_tuple
-	 *
 	 */
 	relid = pg_class_tuple->t_data->t_oid;
 	relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
@@ -1043,37 +1010,31 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 	/*
 	 * allocate storage for the relation descriptor, and copy
 	 * pg_class_tuple to relation->rd_rel.
-	 *
 	 */
 	relation = AllocateRelationDesc(oldrelation, relp);
 
 	/*
 	 * now we can free the memory allocated for pg_class_tuple
-	 *
 	 */
 	heap_freetuple(pg_class_tuple);
 
 	/*
 	 * initialize the relation's relation id (relation->rd_id)
-	 *
 	 */
 	RelationGetRelid(relation) = relid;
 
 	/*
 	 * initialize relation->rd_refcnt
-	 *
 	 */
 	RelationSetReferenceCount(relation, 1);
 
 	/*
 	 * normal relations are not nailed into the cache
-	 *
 	 */
 	relation->rd_isnailed = false;
 
 	/*
 	 * initialize the access method information (relation->rd_am)
-	 *
 	 */
 	relam = relation->rd_rel->relam;
 	if (OidIsValid(relam))
@@ -1082,13 +1043,11 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 
 	/*
 	 * initialize the tuple descriptor (relation->rd_att).
-	 *
 	 */
 	RelationBuildTupleDesc(buildinfo, relation);
 
 	/*
 	 * Fetch rules and triggers that affect this relation
-	 *
 	 */
 	if (relation->rd_rel->relhasrules)
 		RelationBuildRuleLock(relation);
@@ -1105,14 +1064,12 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 
 	/*
 	 * initialize index strategy and support information for this relation
-	 *
 	 */
 	if (OidIsValid(relam))
 		IndexedAccessMethodInitialize(relation);
 
 	/*
 	 * initialize the relation lock manager information
-	 *
 	 */
 	RelationInitLockInfo(relation);		/* see lmgr.c */
 
@@ -1144,7 +1101,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 	/*
 	 * insert newly created relation into proper relcaches, restore memory
 	 * context and return the new reldesc.
-	 *
 	 */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 	RelationCacheInsert(relation);
@@ -1214,11 +1170,14 @@ formrdesc(char *relationName,
 	 * allocate new relation desc
 	 */
 	relation = (Relation) palloc(sizeof(RelationData));
-	MemSet((char *) relation, 0, sizeof(RelationData));
 
 	/*
-	 * don't open the unix file yet..
+	 * clear all fields of reldesc
 	 */
+	MemSet((char *) relation, 0, sizeof(RelationData));
+	relation->rd_targblock = InvalidBlockNumber;
+
+	/* make sure relation is marked as having no open file yet */
 	relation->rd_fd = -1;
 
 	/*
@@ -1329,7 +1288,6 @@ fixrdesc(char *relationName)
 
 	/*
 	 * find the tuple in pg_class corresponding to the given relation name
-	 *
 	 */
 	buildinfo.infotype = INFO_RELNAME;
 	buildinfo.i.info_name = relationName;
@@ -1343,7 +1301,6 @@ fixrdesc(char *relationName)
 
 	/*
 	 * find the pre-made relcache entry (better be there!)
-	 *
 	 */
 	relation = RelationNameCacheGetRelation(relationName);
 	if (!RelationIsValid(relation))
@@ -1353,7 +1310,6 @@ fixrdesc(char *relationName)
 	/*
 	 * and copy pg_class_tuple to relation->rd_rel. (See notes in
 	 * AllocateRelationDesc())
-	 *
 	 */
 	Assert(relation->rd_rel != NULL);
 	memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
@@ -1378,7 +1334,6 @@ fixrdesc(char *relationName)
  *		NB: relation ref count is incremented if successful.
  *		Caller should eventually decrement count.  (Usually,
  *		that happens by calling RelationClose().)
- *
  */
 Relation
 RelationIdCacheGetRelation(Oid relationId)
@@ -1403,7 +1358,6 @@ RelationIdCacheGetRelation(Oid relationId)
  *		RelationNameCacheGetRelation
  *
  *		As above, but lookup by name.
- *
  */
 static Relation
 RelationNameCacheGetRelation(const char *relationName)
@@ -1457,7 +1411,6 @@ RelationNodeCacheGetRelation(RelFileNode rnode)
  *		NB: relation ref count is incremented, or set to 1 if new entry.
  *		Caller should eventually decrement count.  (Usually,
  *		that happens by calling RelationClose().)
- *
  */
 Relation
 RelationIdGetRelation(Oid relationId)
@@ -1467,14 +1420,12 @@ RelationIdGetRelation(Oid relationId)
 
 	/*
 	 * increment access statistics
-	 *
 	 */
 	IncrHeapAccessStat(local_RelationIdGetRelation);
 	IncrHeapAccessStat(global_RelationIdGetRelation);
 
 	/*
 	 * first try and get a reldesc from the cache
-	 *
 	 */
 	rd = RelationIdCacheGetRelation(relationId);
 	if (RelationIsValid(rd))
@@ -1483,7 +1434,6 @@ RelationIdGetRelation(Oid relationId)
 	/*
 	 * no reldesc in the cache, so have RelationBuildDesc() build one and
 	 * add it.
-	 *
 	 */
 	buildinfo.infotype = INFO_RELID;
 	buildinfo.i.info_id = relationId;
@@ -1496,7 +1446,6 @@ RelationIdGetRelation(Oid relationId)
  *		RelationNameGetRelation
  *
  *		As above, but lookup by name.
- *
  */
 Relation
 RelationNameGetRelation(const char *relationName)
@@ -1507,7 +1456,6 @@ RelationNameGetRelation(const char *relationName)
 
 	/*
 	 * increment access statistics
-	 *
 	 */
 	IncrHeapAccessStat(local_RelationNameGetRelation);
 	IncrHeapAccessStat(global_RelationNameGetRelation);
@@ -1515,7 +1463,6 @@ RelationNameGetRelation(const char *relationName)
 	/*
 	 * if caller is looking for a temp relation, substitute its real name;
 	 * we only index temp rels by their real names.
-	 *
 	 */
 	temprelname = get_temp_rel_by_username(relationName);
 	if (temprelname != NULL)
@@ -1523,7 +1470,6 @@ RelationNameGetRelation(const char *relationName)
 
 	/*
 	 * first try and get a reldesc from the cache
-	 *
 	 */
 	rd = RelationNameCacheGetRelation(relationName);
 	if (RelationIsValid(rd))
@@ -1532,7 +1478,6 @@ RelationNameGetRelation(const char *relationName)
 	/*
 	 * no reldesc in the cache, so have RelationBuildDesc() build one and
 	 * add it.
-	 *
 	 */
 	buildinfo.infotype = INFO_RELNAME;
 	buildinfo.i.info_name = (char *) relationName;
@@ -1556,7 +1501,6 @@ RelationNameGetRelation(const char *relationName)
  *	with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test
  *	to catch references to already-released relcache entries.  It slows
  *	things down quite a bit, however.
- *
  */
 void
 RelationClose(Relation relation)
@@ -1577,7 +1521,6 @@ RelationClose(Relation relation)
  *	This function is especially for nailed relations.
  *	relhasindex/relfilenode could be changed even for
  *	nailed relations.
- *
  */
 static void
 RelationReloadClassinfo(Relation relation)
@@ -1616,7 +1559,6 @@ RelationReloadClassinfo(Relation relation)
  *	 usually used when we are notified of a change to an open relation
  *	 (one with refcount > 0).  However, this routine just does whichever
  *	 it's told to do; callers must determine which they want.
- *
  */
 static void
 RelationClearRelation(Relation relation, bool rebuildIt)
@@ -1631,7 +1573,10 @@ RelationClearRelation(Relation relation, bool rebuildIt)
 	 * a vacuum truncation.
 	 */
 	if (relation->rd_fd >= 0)
+	{
 		smgrclose(DEFAULT_SMGR, relation);
+		relation->rd_fd = -1;
+	}
 
 	/*
 	 * Never, never ever blow away a nailed-in system relation, because
@@ -1702,13 +1647,13 @@ RelationClearRelation(Relation relation, bool rebuildIt)
 		 * we save/restore rd_nblocks (in case it is a local relation)
 		 * *and* call RelationGetNumberOfBlocks (in case it isn't).
 		 */
-		uint16		old_refcnt = relation->rd_refcnt;
+		int			old_refcnt = relation->rd_refcnt;
 		bool		old_myxactonly = relation->rd_myxactonly;
 		TupleDesc	old_att = relation->rd_att;
 		RuleLock   *old_rules = relation->rd_rules;
 		MemoryContext old_rulescxt = relation->rd_rulescxt;
 		TriggerDesc *old_trigdesc = relation->trigdesc;
-		int			old_nblocks = relation->rd_nblocks;
+		BlockNumber	old_nblocks = relation->rd_nblocks;
 		RelationBuildDescInfo buildinfo;
 
 		buildinfo.infotype = INFO_RELID;
@@ -1767,7 +1712,6 @@ RelationClearRelation(Relation relation, bool rebuildIt)
  * RelationFlushRelation
  *
  *	 Rebuild the relation if it is open (refcount > 0), else blow it away.
- *
  */
 static void
 RelationFlushRelation(Relation relation)
@@ -1801,7 +1745,6 @@ RelationFlushRelation(Relation relation)
  *		   RelationClearRelation + if the relation is myxactonly then
  *		   remove the relation descriptor from the newly created
  *		   relation list.
- *
  */
 void
 RelationForgetRelation(Oid rid)
@@ -1851,7 +1794,6 @@ RelationForgetRelation(Oid rid)
  *		safer to process them, so that our *own* SI update messages will
  *		have the same effects during CommandCounterIncrement for both
  *		local and nonlocal relations.
- *
  */
 void
 RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
@@ -1989,7 +1931,6 @@ RelationCacheAbortWalker(Relation *relationPtr, Datum dummy)
  *		RelationRegisterRelation -
  *		   register the Relation descriptor of a newly created relation
  *		   with the relation descriptor Cache.
- *
  */
 void
 RelationRegisterRelation(Relation relation)
@@ -2048,7 +1989,6 @@ RelationPurgeLocalRelation(bool xactCommitted)
  *		RelationCacheInitialize
  *
  *		This initializes the relation descriptor cache.
- *
  */
 
 #define INITRELCACHESIZE		400
@@ -2061,7 +2001,6 @@ RelationCacheInitialize(void)
 
 	/*
 	 * switch to cache memory context
-	 *
 	 */
 	if (!CacheMemoryContext)
 		CreateCacheMemoryContext();
@@ -2070,7 +2009,6 @@ RelationCacheInitialize(void)
 
 	/*
 	 * create global caches
-	 *
 	 */
 	MemSet(&ctl, 0, (int) sizeof(ctl));
 	ctl.keysize = sizeof(NameData);
@@ -2093,7 +2031,6 @@ RelationCacheInitialize(void)
 	 * be in the cache.
 	 *
 	 * NB: see also the list in RelationCacheInitializePhase2().
-	 *
 	 */
 	formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class);
 	formrdesc(AttributeRelationName, Natts_pg_attribute, Desc_pg_attribute);
@@ -2115,7 +2052,6 @@ RelationCacheInitialize(void)
  *
  *		This completes initialization of the relcache after catcache
  *		is functional and we are able to actually load data from pg_class.
- *
  */
 void
 RelationCacheInitializePhase2(void)
@@ -2658,7 +2594,8 @@ init_irels(void)
 			return;
 		}
 
-		/* the file descriptor is not yet opened */
+		/* reset transient fields */
+		ird->rd_targblock = InvalidBlockNumber;
 		ird->rd_fd = -1;
 
 		ird->rd_node.tblNode = MyDatabaseId;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2030c7da370..c38d98d3911 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4,7 +4,7 @@
  * Support for grand unified configuration scheme, including SET
  * command, configuration file, and command line options.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.42 2001/06/23 22:23:49 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.43 2001/06/27 23:31:39 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -31,6 +31,8 @@
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "parser/parse_expr.h"
+#include "storage/freespace.h"
+#include "storage/lock.h"
 #include "storage/proc.h"
 #include "tcop/tcopprot.h"
 #include "utils/datetime.h"
@@ -270,11 +272,16 @@ static struct config_int
 	 */
 	{"max_connections", PGC_POSTMASTER, &MaxBackends,
 	DEF_MAXBACKENDS, 1, MAXBACKENDS, NULL, NULL},
+
 	{"shared_buffers", PGC_POSTMASTER, &NBuffers,
 	DEF_NBUFFERS, 16, INT_MAX, NULL, NULL},
+
 	{"port", PGC_POSTMASTER, &PostPortNumber,
 	DEF_PGPORT, 1, 65535, NULL, NULL},
 
+	{"unix_socket_permissions", PGC_POSTMASTER, &Unix_socket_permissions,
+	0777, 0000, 0777, NULL, NULL},
+
 	{"sort_mem", PGC_USERSET, &SortMem,
 	512, 4*BLCKSZ/1024, INT_MAX, NULL, NULL},
 
@@ -290,8 +297,13 @@ static struct config_int
 	{"max_expr_depth", PGC_USERSET, &max_expr_depth,
 	DEFAULT_MAX_EXPR_DEPTH, 10, INT_MAX, NULL, NULL},
 
-	{"unix_socket_permissions", PGC_POSTMASTER, &Unix_socket_permissions,
-	0777, 0000, 0777, NULL, NULL},
+	{"max_fsm_relations", PGC_POSTMASTER, &MaxFSMRelations,
+	 100, 10, INT_MAX, NULL, NULL},
+	{"max_fsm_pages", PGC_POSTMASTER, &MaxFSMPages,
+	 10000, 1000, INT_MAX, NULL, NULL},
+
+	{"max_locks_per_xact", PGC_POSTMASTER, &max_locks_per_xact,
+	 64, 10, INT_MAX, NULL, NULL},
 
 	{"checkpoint_segments", PGC_SIGHUP, &CheckPointSegments,
 	3, 1, INT_MAX, NULL, NULL},
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index a77666d9f44..a3042bee83d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -44,11 +44,19 @@
 #krb_server_keyfile = ''
 
 
+#
+#	Shared Memory Size
+#
+#shared_buffers = 64        # 2*max_connections, min 16
+#max_fsm_relations = 100    # min 10
+#max_fsm_pages = 10000      # min 1000
+#max_locks_per_xact = 64    # min 10
+#wal_buffers = 8            # min 4
+
 #
 #	Performance
 #
 #sort_mem = 512
-#shared_buffers = 64	# 2*max_connections, min 16
 #fsync = true
 
 
@@ -78,7 +86,7 @@
 #	GEQO Optimizer Parameters
 #
 #geqo_threshold = 11
-#geqo_pool_size = 0  #default based in tables, range 128-1024
+#geqo_pool_size = 0  #default based on #tables in query, range 128-1024
 #geqo_effort = 1
 #geqo_generations = 0
 #geqo_random_seed = -1 # auto-compute seed
@@ -87,7 +95,6 @@
 #
 #	Write-ahead log (WAL)
 #
-#wal_buffers = 8 # min 4
 #wal_files = 0 # range 0-64
 #wal_sync_method = fsync # fsync or fdatasync or open_sync or open_datasync
 # Note: default wal_sync_method varies across platforms
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 87bb0007aa0..0d9f66d04b3 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: vacuum.h,v 1.35 2001/05/07 00:43:25 tgl Exp $
+ * $Id: vacuum.h,v 1.36 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,11 +15,14 @@
 #define VACUUM_H
 
 #include "nodes/parsenodes.h"
+#include "storage/block.h"
 
 
 /* in commands/vacuum.c */
 extern void vacuum(VacuumStmt *vacstmt);
-extern void vac_update_relstats(Oid relid, long num_pages, double num_tuples,
+extern void vac_update_relstats(Oid relid,
+								BlockNumber num_pages,
+								double num_tuples,
 								bool hasindex);
 /* in commands/analyze.c */
 extern void analyze_rel(Oid relid, VacuumStmt *vacstmt);
diff --git a/src/include/storage/freespace.h b/src/include/storage/freespace.h
new file mode 100644
index 00000000000..083accccab8
--- /dev/null
+++ b/src/include/storage/freespace.h
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * freespace.h
+ *	  POSTGRES free space map for quickly finding free space in relations
+ *
+ *
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: freespace.h,v 1.1 2001/06/27 23:31:39 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FREESPACE_H_
+#define FREESPACE_H_
+
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+#include "storage/spin.h"
+
+
+extern SPINLOCK FreeSpaceLock;
+
+extern int	MaxFSMRelations;
+extern int	MaxFSMPages;
+
+
+/*
+ * function prototypes
+ */
+extern void InitFreeSpaceMap(void);
+extern int	FreeSpaceShmemSize(void);
+
+extern BlockNumber GetPageWithFreeSpace(RelFileNode *rel, Size spaceNeeded);
+extern void RecordFreeSpace(RelFileNode *rel, BlockNumber page,
+							Size spaceAvail);
+extern BlockNumber RecordAndGetPageWithFreeSpace(RelFileNode *rel,
+												 BlockNumber oldPage,
+												 Size oldSpaceAvail,
+												 Size spaceNeeded);
+extern void MultiRecordFreeSpace(RelFileNode *rel,
+								 BlockNumber minPage,
+								 BlockNumber maxPage,
+								 int nPages,
+								 BlockNumber *pages,
+								 Size *spaceAvail);
+extern void FreeSpaceMapForgetRel(RelFileNode *rel);
+
+#ifdef FREESPACE_DEBUG
+extern void DumpFreeSpace(void);
+#endif
+
+#endif	 /* FREESPACE_H */
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index aa685fc8ff6..8ce1a845930 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: ipc.h,v 1.49 2001/03/22 04:01:05 momjian Exp $
+ * $Id: ipc.h,v 1.50 2001/06/27 23:31:39 tgl Exp $
  *
  * Some files that would normally need to include only sys/ipc.h must
  * instead include this file because on Ultrix, sys/ipc.h is not designed
@@ -74,6 +74,7 @@ typedef enum _LockId_
 	LOCKMGRLOCKID,
 	SINVALLOCKID,
 	PROCSTRUCTLOCKID,
+	FREESPACELOCKID,
 
 #ifdef STABLE_MEMORY_STORAGE
 	MMCACHELOCKID,
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 2428a782a67..30a13649e43 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lock.h,v 1.49 2001/06/22 00:04:59 tgl Exp $
+ * $Id: lock.h,v 1.50 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,8 @@ typedef struct proc PROC;
 
 extern SPINLOCK LockMgrLock;
 
+extern int	max_locks_per_xact;
+
 #ifdef LOCK_DEBUG
 extern int	Trace_lock_oidmin;
 extern bool Trace_locks;
@@ -41,19 +43,6 @@ extern bool Debug_deadlocks;
 #endif	 /* LOCK_DEBUG */
 
 
-/* ----------------------
- * The following defines are used to estimate how much shared
- * memory the lock manager is going to require.
- * See LockShmemSize() in lock.c.
- *
- * NLOCKS_PER_XACT - The number of unique objects locked in a transaction
- *					 (this should be configurable!)
- * NLOCKENTS - The maximum number of lock entries in the lock table.
- * ----------------------
- */
-#define NLOCKS_PER_XACT			64
-#define NLOCKENTS(maxBackends)	(NLOCKS_PER_XACT*(maxBackends))
-
 typedef int LOCKMASK;
 
 typedef int LOCKMODE;
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index b6c5af72dec..b4193b5fa82 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: smgr.h,v 1.29 2001/05/10 20:38:49 tgl Exp $
+ * $Id: smgr.h,v 1.30 2001/06/27 23:31:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,8 +43,9 @@ extern int smgrblindwrt(int16 which, RelFileNode rnode,
 extern int smgrblindmarkdirty(int16 which, RelFileNode rnode,
 				   BlockNumber blkno);
 extern int	smgrmarkdirty(int16 which, Relation reln, BlockNumber blkno);
-extern int	smgrnblocks(int16 which, Relation reln);
-extern int	smgrtruncate(int16 which, Relation reln, int nblocks);
+extern BlockNumber smgrnblocks(int16 which, Relation reln);
+extern BlockNumber smgrtruncate(int16 which, Relation reln,
+								BlockNumber nblocks);
 extern int	smgrDoPendingDeletes(bool isCommit);
 extern int	smgrcommit(void);
 extern int	smgrabort(void);
@@ -71,8 +72,8 @@ extern int	mdmarkdirty(Relation reln, BlockNumber blkno);
 extern int mdblindwrt(RelFileNode rnode, BlockNumber blkno,
 		   char *buffer, bool dofsync);
 extern int	mdblindmarkdirty(RelFileNode rnode, BlockNumber blkno);
-extern int	mdnblocks(Relation reln);
-extern int	mdtruncate(Relation reln, int nblocks);
+extern BlockNumber mdnblocks(Relation reln);
+extern BlockNumber mdtruncate(Relation reln, BlockNumber nblocks);
 extern int	mdcommit(void);
 extern int	mdabort(void);
 extern int	mdsync(void);
@@ -95,8 +96,8 @@ extern int mmblindwrt(char *dbname, char *relname, Oid dbid, Oid relid,
 extern int	mmmarkdirty(Relation reln, BlockNumber blkno);
 extern int mmblindmarkdirty(char *dbname, char *relname, Oid dbid, Oid relid,
 				 BlockNumber blkno);
-extern int	mmnblocks(Relation reln);
-extern int	mmtruncate(Relation reln, int nblocks);
+extern BlockNumber mmnblocks(Relation reln);
+extern BlockNumber mmtruncate(Relation reln, BlockNumber nblocks);
 extern int	mmcommit(void);
 extern int	mmabort(void);
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index c2a9d0982f7..ef74317fda5 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.50 2001/06/22 19:16:24 wieck Exp $
+ * $Id: rel.h,v 1.51 2001/06/27 23:31:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,8 +19,9 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "rewrite/prs2lock.h"
-#include "storage/relfilenode.h"
+#include "storage/block.h"
 #include "storage/fd.h"
+#include "storage/relfilenode.h"
 
 /* added to prevent circular dependency.  bjm 1999/11/15 */
 extern char *get_temp_rel_by_physicalname(const char *relname);
@@ -105,9 +106,11 @@ typedef struct	PgStat_Info
 typedef struct RelationData
 {
 	File		rd_fd;			/* open file descriptor, or -1 if none */
-	RelFileNode rd_node;		/* relation file node */
-	int			rd_nblocks;		/* number of blocks in rel */
-	uint16		rd_refcnt;		/* reference count */
+	RelFileNode rd_node;		/* file node (physical identifier) */
+	BlockNumber	rd_nblocks;		/* number of blocks in rel */
+	BlockNumber	rd_targblock;	/* current insertion target block,
+								 * or InvalidBlockNumber */
+	int			rd_refcnt;		/* reference count */
 	bool		rd_myxactonly;	/* rel uses the local buffer mgr */
 	bool		rd_isnailed;	/* rel is nailed in cache */
 	bool		rd_indexfound;	/* true if rd_indexlist is valid */
-- 
GitLab