diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index ca082618568d182b6f178a4cf53daf4e81415868..98b4cc3d519ae1208aa85e5302aa397eba9632e7 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.14 2004/04/01 21:28:43 tgl Exp $
+ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.15 2004/05/08 19:09:24 tgl Exp $
  *
  * Copyright (c) 2001,2002	Tatsuo Ishii
  *
@@ -127,9 +127,10 @@ pgstattuple_real(Relation rel)
 	 */
 	attinmeta = TupleDescGetAttInMetadata(tupdesc);
 
-	nblocks = RelationGetNumberOfBlocks(rel);
 	scan = heap_beginscan(rel, SnapshotAny, 0, NULL);
 
+	nblocks = scan->rs_nblocks;	/* # blocks to be scanned */
+
 	/* scan the relation */
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index bff84588fc20b2a173fb251f4ba3a52d13857683..894980b41e625e12b17b18e364f2f80d20bd777a 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.165 2004/04/21 18:24:23 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.166 2004/05/08 19:09:24 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -73,12 +73,13 @@ static void
 initscan(HeapScanDesc scan, ScanKey key)
 {
 	/*
-	 * Make sure we have up-to-date idea of number of blocks in relation.
+	 * Determine the number of blocks we have to scan.
+	 *
 	 * It is sufficient to do this once at scan start, since any tuples
 	 * added while the scan is in progress will be invisible to my
 	 * transaction anyway...
 	 */
-	scan->rs_rd->rd_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+	scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
 
 	scan->rs_ctup.t_datamcxt = NULL;
 	scan->rs_ctup.t_data = NULL;
@@ -113,12 +114,12 @@ heapgettup(Relation relation,
 		   Buffer *buffer,
 		   Snapshot snapshot,
 		   int nkeys,
-		   ScanKey key)
+		   ScanKey key,
+		   BlockNumber pages)
 {
 	ItemId		lpp;
 	Page		dp;
 	BlockNumber page;
-	BlockNumber pages;
 	int			lines;
 	OffsetNumber lineoff;
 	int			linesleft;
@@ -159,7 +160,7 @@ heapgettup(Relation relation,
 	/*
 	 * return null immediately if relation is empty
 	 */
-	if ((pages = relation->rd_nblocks) == 0)
+	if (pages == 0)
 	{
 		if (BufferIsValid(*buffer))
 			ReleaseBuffer(*buffer);
@@ -832,7 +833,8 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
 			   &(scan->rs_cbuf),
 			   scan->rs_snapshot,
 			   scan->rs_nkeys,
-			   scan->rs_key);
+			   scan->rs_key,
+			   scan->rs_nblocks);
 
 	if (scan->rs_ctup.t_data == NULL && !BufferIsValid(scan->rs_cbuf))
 	{
@@ -1992,7 +1994,8 @@ heap_restrpos(HeapScanDesc scan)
 				   &(scan->rs_cbuf),
 				   scan->rs_snapshot,
 				   0,
-				   NULL);
+				   NULL,
+				   scan->rs_nblocks);
 	}
 }
 
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 57074a036333e398512d75a61b90a533d3ac809f..ededa6231de3bc97d9c61db3d8819d63185f4fb3 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -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.114 2004/04/21 18:24:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.115 2004/05/08 19:09:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -802,12 +802,7 @@ btvacuumcleanup(PG_FUNCTION_ARGS)
 			/*
 			 * Do the physical truncation.
 			 */
-			if (rel->rd_smgr == NULL)
-				rel->rd_smgr = smgropen(rel->rd_node);
-			new_pages = smgrtruncate(rel->rd_smgr, new_pages);
-			rel->rd_nblocks = new_pages;		/* update relcache
-												 * immediately */
-			rel->rd_targblock = InvalidBlockNumber;
+			RelationTruncate(rel, new_pages);
 			num_pages = new_pages;
 		}
 	}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 51b7d31772aff88853ca9a49564809a55dc8262d..94f1fd2a13d2570c1fb5787947e9162f0a254046 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.263 2004/05/05 04:48:45 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.264 2004/05/08 19:09:24 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1979,12 +1979,8 @@ RelationTruncateIndexes(Oid heapId)
 		 */
 		DropRelationBuffers(currentIndex);
 
-		/* Now truncate the actual data and set blocks to zero */
-		if (currentIndex->rd_smgr == NULL)
-			currentIndex->rd_smgr = smgropen(currentIndex->rd_node);
-		smgrtruncate(currentIndex->rd_smgr, 0);
-		currentIndex->rd_nblocks = 0;
-		currentIndex->rd_targblock = InvalidBlockNumber;
+		/* Now truncate the actual data */
+		RelationTruncate(currentIndex, 0);
 
 		/* Initialize the index and rebuild */
 		index_build(heapRelation, currentIndex, indexInfo);
@@ -2028,12 +2024,8 @@ heap_truncate(Oid rid)
 	 */
 	DropRelationBuffers(rel);
 
-	/* Now truncate the actual data and set blocks to zero */
-	if (rel->rd_smgr == NULL)
-		rel->rd_smgr = smgropen(rel->rd_node);
-	smgrtruncate(rel->rd_smgr, 0);
-	rel->rd_nblocks = 0;
-	rel->rd_targblock = InvalidBlockNumber;
+	/* Now truncate the actual data */
+	RelationTruncate(rel, 0);
 
 	/* 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 6a994aa0cccac8b3b17b5f374b6a9090a03575ea..d6d38795df8049bc8e481e556304a28860a92883 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.230 2004/05/08 00:34:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.231 2004/05/08 19:09:24 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1702,12 +1702,8 @@ reindex_index(Oid indexId)
 		 */
 		DropRelationBuffers(iRel);
 
-		/* Now truncate the actual data and set blocks to zero */
-		if (iRel->rd_smgr == NULL)
-			iRel->rd_smgr = smgropen(iRel->rd_node);
-		smgrtruncate(iRel->rd_smgr, 0);
-		iRel->rd_nblocks = 0;
-		iRel->rd_targblock = InvalidBlockNumber;
+		/* Now truncate the actual data */
+		RelationTruncate(iRel, 0);
 	}
 	else
 	{
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index bd82a96b65076c20f43ed192d5c3249175a2034b..d388f521f183383e53b07c0a9d0a9e8f45003f58 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.70 2004/02/15 21:01:39 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.71 2004/05/08 19:09:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -388,7 +388,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
 
 	/*
 	 * If we are running a standalone ANALYZE, update pages/tuples stats
-	 * in pg_class.  We have the accurate page count from heap_beginscan,
+	 * in pg_class.  We know the accurate page count from the smgr,
 	 * but only an approximate number of tuples; therefore, if we are part
 	 * of VACUUM ANALYZE do *not* overwrite the accurate count already
 	 * inserted by VACUUM.  The same consideration applies to indexes.
@@ -396,7 +396,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
 	if (!vacstmt->vacuum)
 	{
 		vac_update_relstats(RelationGetRelid(onerel),
-							onerel->rd_nblocks,
+							RelationGetNumberOfBlocks(onerel),
 							totalrows,
 							hasindex);
 		for (ind = 0; ind < nindexes; ind++)
@@ -657,6 +657,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
 {
 	int			numrows = 0;
 	HeapScanDesc scan;
+	BlockNumber	totalblocks;
 	HeapTuple	tuple;
 	ItemPointer lasttuple;
 	BlockNumber lastblock,
@@ -673,6 +674,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
 	 * Do a simple linear scan until we reach the target number of rows.
 	 */
 	scan = heap_beginscan(onerel, SnapshotNow, 0, NULL);
+	totalblocks = scan->rs_nblocks;	/* grab current relation size */
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
 		rows[numrows++] = heap_copytuple(tuple);
@@ -693,7 +695,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
 		ereport(elevel,
 				(errmsg("\"%s\": %u pages, %d rows sampled, %.0f estimated total rows",
 						RelationGetRelationName(onerel),
-						onerel->rd_nblocks, numrows, *totalrows)));
+						totalblocks, numrows, *totalrows)));
 
 		return numrows;
 	}
@@ -772,10 +774,9 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
 pageloop:;
 
 		/*
-		 * Have we fallen off the end of the relation?	(We rely on
-		 * heap_beginscan to have updated rd_nblocks.)
+		 * Have we fallen off the end of the relation?
 		 */
-		if (targblock >= onerel->rd_nblocks)
+		if (targblock >= totalblocks)
 			break;
 
 		/*
@@ -841,7 +842,7 @@ pageloop:;
 	/*
 	 * Estimate total number of valid rows in relation.
 	 */
-	*totalrows = floor((double) onerel->rd_nblocks * tuplesperpage + 0.5);
+	*totalrows = floor((double) totalblocks * tuplesperpage + 0.5);
 
 	/*
 	 * Emit some interesting relation info 
@@ -849,7 +850,7 @@ pageloop:;
 	ereport(elevel,
 			(errmsg("\"%s\": %u pages, %d rows sampled, %.0f estimated total rows",
 					RelationGetRelationName(onerel),
-					onerel->rd_nblocks, numrows, *totalrows)));
+					totalblocks, numrows, *totalrows)));
 
 	return numrows;
 }
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index de4499124d58a98729d7ccdc9d9c7336eccba7a7..0ff1b386b5b167a7313314c0323315d22782c87a 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.109 2004/04/06 16:39:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.110 2004/05/08 19:09:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -822,10 +822,6 @@ read_info(SeqTable elm, Relation rel, Buffer *buf)
 	sequence_magic *sm;
 	Form_pg_sequence seq;
 
-	if (rel->rd_nblocks > 1)
-		elog(ERROR, "invalid number of blocks in sequence \"%s\"",
-			 RelationGetRelationName(rel));
-
 	*buf = ReadBuffer(rel, 0);
 	if (!BufferIsValid(*buf))
 		elog(ERROR, "ReadBuffer failed");
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 760c10bcc80841e7fbb49dd0de0c470e785a40a0..342c87db574eb434c094c63a7431b3aeba3087e5 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.274 2004/02/12 05:39:55 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.275 2004/05/08 19:09:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2522,11 +2522,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 	/* truncate relation, if needed */
 	if (blkno < nblocks)
 	{
-		if (onerel->rd_smgr == NULL)
-			onerel->rd_smgr = smgropen(onerel->rd_node);
-		blkno = smgrtruncate(onerel->rd_smgr, blkno);
-		onerel->rd_nblocks = blkno;		/* update relcache immediately */
-		onerel->rd_targblock = InvalidBlockNumber;
+		RelationTruncate(onerel, blkno);
 		vacrelstats->rel_pages = blkno; /* set new number of blocks */
 	}
 
@@ -2594,11 +2590,7 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages)
 				(errmsg("\"%s\": truncated %u to %u pages",
 						RelationGetRelationName(onerel),
 						vacrelstats->rel_pages, relblocks)));
-		if (onerel->rd_smgr == NULL)
-			onerel->rd_smgr = smgropen(onerel->rd_node);
-		relblocks = smgrtruncate(onerel->rd_smgr, relblocks);
-		onerel->rd_nblocks = relblocks; /* update relcache immediately */
-		onerel->rd_targblock = InvalidBlockNumber;
+		RelationTruncate(onerel, relblocks);
 		vacrelstats->rel_pages = relblocks;		/* set new number of
 												 * blocks */
 	}
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 46920c0009b4987d9e8a6263370fc8d7f46adb78..af1b646be467e619a558510d3fd6752ad2374f6d 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -31,7 +31,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.39 2004/04/25 23:50:54 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.40 2004/05/08 19:09:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -738,11 +738,7 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
 	/*
 	 * Do the physical truncation.
 	 */
-	if (onerel->rd_smgr == NULL)
-		onerel->rd_smgr = smgropen(onerel->rd_node);
-	new_rel_pages = smgrtruncate(onerel->rd_smgr, new_rel_pages);
-	onerel->rd_nblocks = new_rel_pages; /* update relcache immediately */
-	onerel->rd_targblock = InvalidBlockNumber;
+	RelationTruncate(onerel, new_rel_pages);
 	vacrelstats->rel_pages = new_rel_pages;		/* save new number of
 												 * blocks */
 
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 64f5e5ad40a0afd7c3775839c9c0be71f10a41c7..4f6772564a8df76148624befcdb5997c7a723138 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.164 2004/04/25 23:50:54 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.165 2004/05/08 19:09:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -98,8 +98,6 @@ static void write_buffer(Buffer buffer, bool unpin);
  *
  * Assume when this function is called, that reln has been
  *		opened already.
- *
- * Note: a side effect of a P_NEW call is to update reln->rd_nblocks.
  */
 Buffer
 ReadBuffer(Relation reln, BlockNumber blockNum)
@@ -129,14 +127,14 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
 	if (reln->rd_smgr == NULL)
 		reln->rd_smgr = smgropen(reln->rd_node);
 
+	/* Substitute proper block number if caller asked for P_NEW */
+	if (isExtend)
+		blockNum = smgrnblocks(reln->rd_smgr);
+
 	if (isLocalBuf)
 	{
 		ReadLocalBufferCount++;
 		pgstat_count_buffer_read(&reln->pgstat_info, reln);
-		/* Substitute proper block number if caller asked for P_NEW */
-		if (isExtend)
-			blockNum = reln->rd_nblocks;
-
 		bufHdr = LocalBufferAlloc(reln, blockNum, &found);
 		if (found)
 			LocalBufferHitCount++;
@@ -145,13 +143,6 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
 	{
 		ReadBufferCount++;
 		pgstat_count_buffer_read(&reln->pgstat_info, reln);
-		/* Substitute proper block number if caller asked for P_NEW */
-		if (isExtend)
-		{
-			/* must be sure we have accurate file length! */
-			blockNum = reln->rd_nblocks = smgrnblocks(reln->rd_smgr);
-		}
-
 		/*
 		 * lookup the buffer.  IO_IN_PROGRESS is set if the requested
 		 * block is not currently in memory.
@@ -196,8 +187,6 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
 		/* new buffers are zero-filled */
 		MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
 		smgrextend(reln->rd_smgr, blockNum, (char *) MAKE_PTR(bufHdr->data));
-		/* After successful extend, increment relation length */
-		reln->rd_nblocks++;
 	}
 	else
 	{
@@ -1085,55 +1074,36 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
 /*
  * RelationGetNumberOfBlocks
  *		Determines the current number of pages in the relation.
- *		Side effect: relation->rd_nblocks is updated.
  */
 BlockNumber
 RelationGetNumberOfBlocks(Relation relation)
 {
-	/*
-	 * relation->rd_nblocks should be accurate already if the relation is
-	 * new or temp, because no one else should be modifying it.  Otherwise
-	 * we need to ask the smgr for the current physical file length.
-	 *
-	 * Don't call smgr on a view or a composite type, either.
-	 */
-	if (relation->rd_rel->relkind == RELKIND_VIEW ||
-		relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
-		relation->rd_nblocks = 0;
-	else if (!relation->rd_isnew && !relation->rd_istemp)
-	{
-		/* Open it at the smgr level if not already done */
-		if (relation->rd_smgr == NULL)
-			relation->rd_smgr = smgropen(relation->rd_node);
-
-		relation->rd_nblocks = smgrnblocks(relation->rd_smgr);
-	}
+	/* Open it at the smgr level if not already done */
+	if (relation->rd_smgr == NULL)
+		relation->rd_smgr = smgropen(relation->rd_node);
 
-	return relation->rd_nblocks;
+	return smgrnblocks(relation->rd_smgr);
 }
 
 /*
- * RelationUpdateNumberOfBlocks
- *		Forcibly update relation->rd_nblocks.
+ * RelationTruncate
+ *		Physically truncate a relation to the specified number of blocks.
  *
- * If the relcache drops an entry for a temp relation, it must call this
- * routine after recreating the relcache entry, so that rd_nblocks is
- * re-sync'd with reality.  See RelationGetNumberOfBlocks.
+ * Caller should already have done something to flush any buffered pages
+ * that are to be dropped.
  */
 void
-RelationUpdateNumberOfBlocks(Relation relation)
+RelationTruncate(Relation rel, BlockNumber nblocks)
 {
-	if (relation->rd_rel->relkind == RELKIND_VIEW ||
-		relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
-		relation->rd_nblocks = 0;
-	else
-	{
-		/* Open it at the smgr level if not already done */
-		if (relation->rd_smgr == NULL)
-			relation->rd_smgr = smgropen(relation->rd_node);
+	/* Open it at the smgr level if not already done */
+	if (rel->rd_smgr == NULL)
+		rel->rd_smgr = smgropen(rel->rd_node);
 
-		relation->rd_nblocks = smgrnblocks(relation->rd_smgr);
-	}
+	/* Make sure rd_targblock isn't pointing somewhere past end */
+	rel->rd_targblock = InvalidBlockNumber;
+
+	/* Do the real work */
+	smgrtruncate(rel->rd_smgr, nblocks);
 }
 
 /* ---------------------------------------------------------------------
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 85ad6ffe78867490ac8ad45a0c75991dbe351c35..0e4a31745fda0096218b549cd1e006c7bed8d3c5 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.201 2004/04/01 21:28:45 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.202 2004/05/08 19:09:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -889,17 +889,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 	RelationCacheInsert(relation);
 	MemoryContextSwitchTo(oldcxt);
 
-	/*
-	 * If it's a temp rel, RelationGetNumberOfBlocks will assume that
-	 * rd_nblocks is correct.  Must forcibly update the block count when
-	 * creating the relcache entry.  But if we are doing a rebuild, don't
-	 * do this yet; leave it to RelationClearRelation to do at the end.
-	 * (Otherwise, an elog in RelationUpdateNumberOfBlocks would leave us
-	 * with inconsistent relcache state.)
-	 */
-	if (relation->rd_istemp && oldrelation == NULL)
-		RelationUpdateNumberOfBlocks(relation);
-
 	return relation;
 }
 
@@ -1583,9 +1572,7 @@ RelationReloadClassinfo(Relation relation)
 	memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
 	relation->rd_node.relNode = relp->relfilenode;
 	heap_freetuple(pg_class_tuple);
-	/* Must adjust number of blocks after we know the new relfilenode */
 	relation->rd_targblock = InvalidBlockNumber;
-	RelationUpdateNumberOfBlocks(relation);
 	/* Okay, now it's valid again */
 	relation->rd_isnailed = 1;
 }
@@ -1620,29 +1607,26 @@ RelationClearRelation(Relation relation, bool rebuild)
 
 	/*
 	 * Never, never ever blow away a nailed-in system relation, because
-	 * we'd be unable to recover.  However, we must update rd_nblocks and
-	 * reset rd_targblock, in case we got called because of a relation
-	 * cache flush that was triggered by VACUUM.  If it's a nailed index,
-	 * then we need to re-read the pg_class row to see if its relfilenode
-	 * changed.  We can't necessarily do that here, because we might be in
-	 * a failed transaction.  We assume it's okay to do it if there are open
-	 * references to the relcache entry (cf notes for AtEOXact_RelationCache).
-	 * Otherwise just mark the entry as possibly invalid, and it'll be fixed
-	 * when next opened.
+	 * we'd be unable to recover.  However, we must reset rd_targblock, in
+	 * case we got called because of a relation cache flush that was triggered
+	 * by VACUUM.
+	 *
+	 * If it's a nailed index, then we need to re-read the pg_class row to see
+	 * if its relfilenode changed.  We can't necessarily do that here, because
+	 * we might be in a failed transaction.  We assume it's okay to do it if
+	 * there are open references to the relcache entry (cf notes for
+	 * AtEOXact_RelationCache).  Otherwise just mark the entry as possibly
+	 * invalid, and it'll be fixed when next opened.
 	 */
 	if (relation->rd_isnailed)
 	{
+		relation->rd_targblock = InvalidBlockNumber;
 		if (relation->rd_rel->relkind == RELKIND_INDEX)
 		{
 			relation->rd_isnailed = 2;	/* needs to be revalidated */
 			if (relation->rd_refcnt > 1)
 				RelationReloadClassinfo(relation);
 		}
-		else
-		{
-			relation->rd_targblock = InvalidBlockNumber;
-			RelationUpdateNumberOfBlocks(relation);
-		}
 		return;
 	}
 
@@ -1746,15 +1730,6 @@ RelationClearRelation(Relation relation, bool rebuild)
 			if (old_rulescxt)
 				MemoryContextDelete(old_rulescxt);
 		}
-
-		/*
-		 * Update rd_nblocks.  This is kind of expensive, but I think we
-		 * must do it in case relation has been truncated... we definitely
-		 * must do it if the rel is new or temp, since
-		 * RelationGetNumberOfBlocks will subsequently assume that the
-		 * block count is correct.
-		 */
-		RelationUpdateNumberOfBlocks(relation);
 	}
 }
 
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index 746b795b26eaf5f749056419f1ae592cb06f0ae0..b6ca7e43a62d87f7569effa770a39d165124f432 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.34 2003/11/29 22:40:55 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.35 2004/05/08 19:09:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,7 @@ typedef struct HeapScanDescData
 	Snapshot	rs_snapshot;	/* snapshot to see */
 	int			rs_nkeys;		/* number of scan keys */
 	ScanKey		rs_key;			/* array of scan key descriptors */
+	BlockNumber rs_nblocks;		/* number of blocks to scan */
 
 	/* scan current state */
 	HeapTupleData rs_ctup;		/* current tuple in scan, if any */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 1cd16a515342582d0d1b38323a43a2bc2df80599..0aab9ad24f4466d39e3a2354cae535d0d031ad73 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.78 2004/04/25 23:50:58 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.79 2004/05/08 19:09:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -156,7 +156,7 @@ extern void AtEOXact_Buffers(bool isCommit);
 extern void FlushBufferPool(void);
 extern BlockNumber BufferGetBlockNumber(Buffer buffer);
 extern BlockNumber RelationGetNumberOfBlocks(Relation relation);
-extern void RelationUpdateNumberOfBlocks(Relation relation);
+extern void RelationTruncate(Relation rel, BlockNumber nblocks);
 extern int	FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock);
 extern void DropRelationBuffers(Relation rel);
 extern void DropRelFileNodeBuffers(RelFileNode rnode, bool istemp);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 8532c5a737abf689a959cc904375167881e2b827..e5008e56ea5276c42948d02d15eae7bc2a96ee0d 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.73 2004/02/10 01:55:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.74 2004/05/08 19:09:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,7 +107,6 @@ typedef struct RelationData
 	RelFileNode rd_node;		/* relation physical identifier */
 	/* use "struct" here to avoid needing to include smgr.h: */
 	struct SMgrRelationData *rd_smgr; /* cached file handle, or NULL */
-	BlockNumber rd_nblocks;		/* number of blocks in rel */
 	BlockNumber rd_targblock;	/* current insertion target block, or
 								 * InvalidBlockNumber */
 	int			rd_refcnt;		/* reference count */