diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 1372bb638f..75b6d96440 100644
*** a/contrib/pg_visibility/pg_visibility.c
--- b/contrib/pg_visibility/pg_visibility.c
***************
*** 383,388 **** pg_truncate_visibility_map(PG_FUNCTION_ARGS)
--- 383,390 ----
  {
  	Oid			relid = PG_GETARG_OID(0);
  	Relation	rel;
+ 	ForkNumber	fork;
+ 	BlockNumber	block;
  
  	rel = relation_open(relid, AccessExclusiveLock);
  
***************
*** 392,398 **** pg_truncate_visibility_map(PG_FUNCTION_ARGS)
  	RelationOpenSmgr(rel);
  	rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
  
! 	visibilitymap_truncate(rel, 0);
  
  	if (RelationNeedsWAL(rel))
  	{
--- 394,405 ----
  	RelationOpenSmgr(rel);
  	rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
  
! 	block = visibilitymap_prepare_truncate(rel, 0);
! 	if (BlockNumberIsValid(block))
! 	{
! 		fork = VISIBILITYMAP_FORKNUM;
! 		smgrtruncate(rel->rd_smgr, &fork, 1, &block);
! 	}
  
  	if (RelationNeedsWAL(rel))
  	{
***************
*** 418,424 **** pg_truncate_visibility_map(PG_FUNCTION_ARGS)
  	 * here and when we sent the messages at our eventual commit.  However,
  	 * we're currently only sending a non-transactional smgr invalidation,
  	 * which will have been posted to shared memory immediately from within
! 	 * visibilitymap_truncate.  Therefore, there should be no race here.
  	 *
  	 * The reason why it's desirable to release the lock early here is because
  	 * of the possibility that someone will need to use this to blow away many
--- 425,431 ----
  	 * here and when we sent the messages at our eventual commit.  However,
  	 * we're currently only sending a non-transactional smgr invalidation,
  	 * which will have been posted to shared memory immediately from within
! 	 * smgr_truncate.  Therefore, there should be no race here.
  	 *
  	 * The reason why it's desirable to release the lock early here is because
  	 * of the possibility that someone will need to use this to blow away many
diff --git a/src/backend/access/heap/visibilindex a08922b079..262876772f 100644
*** a/src/backend/access/heap/visibilitymap.c
--- b/src/backend/access/heap/visibilitymap.c
***************
*** 17,23 ****
   *		visibilitymap_set	 - set a bit in a previously pinned page
   *		visibilitymap_get_status - get status of bits
   *		visibilitymap_count  - count number of bits set in visibility map
!  *		visibilitymap_truncate	- truncate the visibility map
   *
   * NOTES
   *
--- 17,24 ----
   *		visibilitymap_set	 - set a bit in a previously pinned page
   *		visibilitymap_get_status - get status of bits
   *		visibilitymap_count  - count number of bits set in visibility map
!  *		visibilitymap_prepare_truncate -
!  *			prepare for truncation of the visibility map
   *
   * NOTES
   *
***************
*** 430,445 **** visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_fro
  }
  
  /*
!  *	visibilitymap_truncate - truncate the visibility map
!  *
!  * The caller must hold AccessExclusiveLock on the relation, to ensure that
!  * other backends receive the smgr invalidation event that this function sends
!  * before they access the VM again.
   *
   * nheapblocks is the new size of the heap.
   */
! void
! visibilitymap_truncate(Relation rel, BlockNumber nheapblocks)
  {
  	BlockNumber newnblocks;
  
--- 431,448 ----
  }
  
  /*
!  *	visibilitymap_prepare_truncate -
!  *			prepare for truncation of the visibility map
   *
   * nheapblocks is the new size of the heap.
+  *
+  * Return the number of blocks of new visibility map after it's truncated.
+  * If it's InvalidBlockNumber, there is nothing to truncate;
+  * otherwise the caller is responsible for calling smgrtruncate()
+  * to truncate the visibility map pages.
   */
! BlockNumber
! visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks)
  {
  	BlockNumber newnblocks;
  
***************
*** 459,465 **** visibilitymap_truncate(Relation rel, BlockNumber nheapblocks)
  	 * nothing to truncate.
  	 */
  	if (!smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM))
! 		return;
  
  	/*
  	 * Unless the new size is exactly at a visibility map page boundary, the
--- 462,468 ----
  	 * nothing to truncate.
  	 */
  	if (!smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM))
! 		return InvalidBlockNumber;
  
  	/*
  	 * Unless the new size is exactly at a visibility map page boundary, the
***************
*** 480,486 **** visibilitymap_truncate(Relation rel, BlockNumber nheapblocks)
  		if (!BufferIsValid(mapBuffer))
  		{
  			/* nothing to do, the file was already smaller */
! 			return;
  		}
  
  		page = BufferGetPage(mapBuffer);
--- 483,489 ----
  		if (!BufferIsValid(mapBuffer))
  		{
  			/* nothing to do, the file was already smaller */
! 			return InvalidBlockNumber;
  		}
  
  		page = BufferGetPage(mapBuffer);
***************
*** 528,547 **** visibilitymap_truncate(Relation rel, BlockNumber nheapblocks)
  	if (smgrnblocks(rel->rd_smgr, VISIBILITYMAP_FORKNUM) <= newnblocks)
  	{
  		/* nothing to do, the file was already smaller than requested size */
! 		return;
  	}
  
! 	/* Truncate the unused VM pages, and send smgr inval message */
! 	smgrtruncate(rel->rd_smgr, VISIBILITYMAP_FORKNUM, newnblocks);
! 
! 	/*
! 	 * We might as well update the local smgr_vm_nblocks setting. smgrtruncate
! 	 * sent an smgr cache inval message, which will cause other backends to
! 	 * invalidate their copy of smgr_vm_nblocks, and this one too at the next
! 	 * command boundary.  But this ensures it isn't outright wrong until then.
! 	 */
! 	if (rel->rd_smgr)
! 		rel->rd_smgr->smgr_vm_nblocks = newnblocks;
  }
  
  /*
--- 531,540 ----
  	if (smgrnblocks(rel->rd_smgr, VISIBILITYMAP_FORKNUM) <= newnblocks)
  	{
  		/* nothing to do, the file was already smaller than requested size */
! 		return InvalidBlockNumber;
  	}
  
! 	return newnblocks;
  }
  
  /*
diff --git a/src/backend/catalog/storage.c b/sindex 3cc886f7fe..b8c9b6f9c6 100644
*** a/src/backend/catalog/storage.c
--- b/src/backend/catalog/storage.c
***************
*** 231,236 **** RelationTruncate(Relation rel, BlockNumber nblocks)
--- 231,240 ----
  {
  	bool		fsm;
  	bool		vm;
+ 	bool		need_fsm_vacuum = false;
+ 	ForkNumber	forks[MAX_FORKNUM];
+ 	BlockNumber	blocks[MAX_FORKNUM];
+ 	int		nforks = 0;
  
  	/* Open it at the smgr level if not already done */
  	RelationOpenSmgr(rel);
***************
*** 242,256 **** RelationTruncate(Relation rel, BlockNumber nblocks)
  	rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber;
  	rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
  
! 	/* Truncate the FSM first if it exists */
  	fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM);
  	if (fsm)
! 		FreeSpaceMapTruncateRel(rel, nblocks);
  
! 	/* Truncate the visibility map too if it exists. */
  	vm = smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM);
  	if (vm)
! 		visibilitymap_truncate(rel, nblocks);
  
  	/*
  	 * We WAL-log the truncation before actually truncating, which means
--- 246,280 ----
  	rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber;
  	rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
  
! 	/* Prepare for truncation of MAIN fork of the relation */
! 	forks[nforks] = MAIN_FORKNUM;
! 	blocks[nforks] = nblocks;
! 	nforks++;
! 
! 	/*  Prepare for truncation of the FSM if it exists */
  	fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM);
  	if (fsm)
! 	{
! 		blocks[nforks] = FreeSpaceMapPrepareTruncateRel(rel, nblocks);
! 		if (BlockNumberIsValid(blocks[nforks]))
! 		{
! 			forks[nforks] = FSM_FORKNUM;
! 			nforks++;
! 			need_fsm_vacuum = true;
! 		}
! 	}
  
! 	/* Prepare for truncation of the visibility map too if it exists */
  	vm = smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM);
  	if (vm)
! 	{
! 		blocks[nforks] = visibilitymap_prepare_truncate(rel, nblocks);
! 		if (BlockNumberIsValid(blocks[nforks]))
! 		{
! 			forks[nforks] = VISIBILITYMAP_FORKNUM;
! 			nforks++;
! 		}
! 	}
  
  	/*
  	 * We WAL-log the truncation before actually truncating, which means
***************
*** 290,297 **** RelationTruncate(Relation rel, BlockNumber nblocks)
  			XLogFlush(lsn);
  	}
  
! 	/* Do the real work */
! 	smgrtruncate(rel->rd_smgr, MAIN_FORKNUM, nblocks);
  }
  
  /*
--- 314,329 ----
  			XLogFlush(lsn);
  	}
  
! 	/* Do the real work to truncate relation forks */
! 	smgrtruncate(rel->rd_smgr, forks, nforks, blocks);
! 
! 	/*
! 	 * Update upper-level FSM pages to account for the truncation.
! 	 * This is important because the just-truncated pages were likely
! 	 * marked as all-free, and would be preferentially selected.
! 	 */
! 	if (need_fsm_vacuum)
! 		FreeSpaceMapVacuumRange(rel, nblocks, InvalidBlockNumber);
  }
  
  /*
***************
*** 588,593 **** smgr_redo(XLogReaderState *record)
--- 620,629 ----
  		xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record);
  		SMgrRelation reln;
  		Relation	rel;
+ 		ForkNumber	forks[MAX_FORKNUM];
+ 		BlockNumber	blocks[MAX_FORKNUM];
+ 		int		nforks = 0;
+ 		bool		need_fsm_vacuum = false;
  
  		reln = smgropen(xlrec->rnode, InvalidBackendId);
  
***************
*** 616,638 **** smgr_redo(XLogReaderState *record)
  		 */
  		XLogFlush(lsn);
  
  		if ((xlrec->flags & SMGR_TRUNCATE_HEAP) != 0)
  		{
! 			smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno);
  
  			/* Also tell xlogutils.c about it */
  			XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno);
  		}
  
! 		/* Truncate FSM and VM too */
  		rel = CreateFakeRelcacheEntry(xlrec->rnode);
  
  		if ((xlrec->flags & SMGR_TRUNCATE_FSM) != 0 &&
  			smgrexists(reln, FSM_FORKNUM))
! 			FreeSpaceMapTruncateRel(rel, xlrec->blkno);
  		if ((xlrec->flags & SMGR_TRUNCATE_VM) != 0 &&
  			smgrexists(reln, VISIBILITYMAP_FORKNUM))
! 			visibilitymap_truncate(rel, xlrec->blkno);
  
  		FreeFakeRelcacheEntry(rel);
  	}
--- 652,705 ----
  		 */
  		XLogFlush(lsn);
  
+ 		/* Prepare for truncation of MAIN fork */
  		if ((xlrec->flags & SMGR_TRUNCATE_HEAP) != 0)
  		{
! 			forks[nforks] = MAIN_FORKNUM;
! 			blocks[nforks] = xlrec->blkno;
! 			nforks++;
  
  			/* Also tell xlogutils.c about it */
  			XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno);
  		}
  
! 		/* Prepare for truncation of FSM and VM too */
  		rel = CreateFakeRelcacheEntry(xlrec->rnode);
  
  		if ((xlrec->flags & SMGR_TRUNCATE_FSM) != 0 &&
  			smgrexists(reln, FSM_FORKNUM))
! 		{
! 			blocks[nforks] = FreeSpaceMapPrepareTruncateRel(rel, xlrec->blkno);
! 			if (BlockNumberIsValid(blocks[nforks]))
! 			{
! 				forks[nforks] = FSM_FORKNUM;
! 				nforks++;
! 				need_fsm_vacuum = true;
! 			}
! 		}
  		if ((xlrec->flags & SMGR_TRUNCATE_VM) != 0 &&
  			smgrexists(reln, VISIBILITYMAP_FORKNUM))
! 		{
! 			blocks[nforks] = visibilitymap_prepare_truncate(rel, xlrec->blkno);
! 			if (BlockNumberIsValid(blocks[nforks]))
! 			{
! 				forks[nforks] = VISIBILITYMAP_FORKNUM;
! 				nforks++;
! 			}
! 		}
! 
! 		/* Do the real work to truncate relation forks */
! 		if (nforks > 0)
! 			smgrtruncate(reln, forks, nforks, blocks);
! 
! 		/*
! 		 * Update upper-level FSM pages to account for the truncation.
! 		 * This is important because the just-truncated pages were likely
! 		 * marked as all-free, and would be preferentially selected.
! 		 */
! 		if (need_fsm_vacuum)
! 			FreeSpaceMapVacuumRange(rel, xlrec->blkno,
! 									InvalidBlockNumber);
  
  		FreeFakeRelcacheEntry(rel);
  	}
diff --git a/src/backend/storage/bufindex 6f3a402854..483f705305 100644
*** a/src/backend/storage/buffer/bufmgr.c
--- b/src/backend/storage/buffer/bufmgr.c
***************
*** 2901,2907 **** BufferGetLSNAtomic(Buffer buffer)
   *		DropRelFileNodeBuffers
   *
   *		This function removes from the buffer pool all the pages of the
!  *		specified relation fork that have block numbers >= firstDelBlock.
   *		(In particular, with firstDelBlock = 0, all pages are removed.)
   *		Dirty pages are simply dropped, without bothering to write them
   *		out first.  Therefore, this is NOT rollback-able, and so should be
--- 2901,2907 ----
   *		DropRelFileNodeBuffers
   *
   *		This function removes from the buffer pool all the pages of the
!  *		specified relation forks that have block numbers >= firstDelBlock.
   *		(In particular, with firstDelBlock = 0, all pages are removed.)
   *		Dirty pages are simply dropped, without bothering to write them
   *		out first.  Therefore, this is NOT rollback-able, and so should be
***************
*** 2924,2939 **** BufferGetLSNAtomic(Buffer buffer)
   * --------------------------------------------------------------------
   */
  void
! DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber forkNum,
! 					   BlockNumber firstDelBlock)
  {
  	int			i;
  
  	/* If it's a local relation, it's localbuf.c's problem. */
  	if (RelFileNodeBackendIsTemp(rnode))
  	{
  		if (rnode.backend == MyBackendId)
! 			DropRelFileNodeLocalBuffers(rnode.node, forkNum, firstDelBlock);
  		return;
  	}
  
--- 2924,2944 ----
   * --------------------------------------------------------------------
   */
  void
! DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber *forkNum,
! 					   int nforks, BlockNumber *firstDelBlock)
  {
  	int			i;
+ 	int			j;
  
  	/* If it's a local relation, it's localbuf.c's problem. */
  	if (RelFileNodeBackendIsTemp(rnode))
  	{
  		if (rnode.backend == MyBackendId)
! 		{
! 			for (j = 0; j < nforks; j++)
! 				DropRelFileNodeLocalBuffers(rnode.node, forkNum[j],
! 											firstDelBlock[j]);
! 		}
  		return;
  	}
  
***************
*** 2962,2972 **** DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber forkNum,
  			continue;
  
  		buf_state = LockBufHdr(bufHdr);
! 		if (RelFileNodeEquals(bufHdr->tag.rnode, rnode.node) &&
! 			bufHdr->tag.forkNum == forkNum &&
! 			bufHdr->tag.blockNum >= firstDelBlock)
! 			InvalidateBuffer(bufHdr);	/* releases spinlock */
! 		else
  			UnlockBufHdr(bufHdr, buf_state);
  	}
  }
--- 2967,2984 ----
  			continue;
  
  		buf_state = LockBufHdr(bufHdr);
! 
! 		for (j = 0; j < nforks; j++)
! 		{
! 			if (RelFileNodeEquals(bufHdr->tag.rnode, rnode.node) &&
! 				bufHdr->tag.forkNum == forkNum[j] &&
! 				bufHdr->tag.blockNum >= firstDelBlock[j])
! 			{
! 				InvalidateBuffer(bufHdr); /* releases spinlock */
! 				break;
! 			}
! 		}
! 		if (j >= nforks)
  			UnlockBufHdr(bufHdr, buf_state);
  	}
  }
diff --git a/src/backend/storage/freespaceindex 2383094cfd..b7a048c96c 100644
*** a/src/backend/storage/freespace/freespace.c
--- b/src/backend/storage/freespace/freespace.c
***************
*** 247,262 **** GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk)
  }
  
  /*
!  * FreeSpaceMapTruncateRel - adjust for truncation of a relation.
!  *
!  * The caller must hold AccessExclusiveLock on the relation, to ensure that
!  * other backends receive the smgr invalidation event that this function sends
!  * before they access the FSM again.
   *
   * nblocks is the new size of the heap.
   */
! void
! FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks)
  {
  	BlockNumber new_nfsmblocks;
  	FSMAddress	first_removed_address;
--- 247,264 ----
  }
  
  /*
!  * FreeSpaceMapPrepareTruncateRel - prepare for truncation of a relation.
   *
   * nblocks is the new size of the heap.
+  *
+  * Return the number of blocks of new FSM after it's truncated.
+  * If it's InvalidBlockNumber, there is nothing to truncate;
+  * otherwise the caller is responsible for calling smgrtruncate()
+  * to truncate the FSM pages, and FreeSpaceMapVacuumRange()
+  * to update upper-level pages in the FSM.
   */
! BlockNumber
! FreeSpaceMapPrepareTruncateRel(Relation rel, BlockNumber nblocks)
  {
  	BlockNumber new_nfsmblocks;
  	FSMAddress	first_removed_address;
***************
*** 270,276 **** FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks)
  	 * truncate.
  	 */
  	if (!smgrexists(rel->rd_smgr, FSM_FORKNUM))
! 		return;
  
  	/* Get the location in the FSM of the first removed heap block */
  	first_removed_address = fsm_get_location(nblocks, &first_removed_slot);
--- 272,278 ----
  	 * truncate.
  	 */
  	if (!smgrexists(rel->rd_smgr, FSM_FORKNUM))
! 		return InvalidBlockNumber;
  
  	/* Get the location in the FSM of the first removed heap block */
  	first_removed_address = fsm_get_location(nblocks, &first_removed_slot);
***************
*** 285,291 **** FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks)
  	{
  		buf = fsm_readbuf(rel, first_removed_address, false);
  		if (!BufferIsValid(buf))
! 			return;				/* nothing to do; the FSM was already smaller */
  		LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
  
  		/* NO EREPORT(ERROR) from here till changes are logged */
--- 287,293 ----
  	{
  		buf = fsm_readbuf(rel, first_removed_address, false);
  		if (!BufferIsValid(buf))
! 			return InvalidBlockNumber;	/* nothing to do; the FSM was already smaller */
  		LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
  
  		/* NO EREPORT(ERROR) from here till changes are logged */
***************
*** 315,342 **** FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks)
  	{
  		new_nfsmblocks = fsm_logical_to_physical(first_removed_address);
  		if (smgrnblocks(rel->rd_smgr, FSM_FORKNUM) <= new_nfsmblocks)
! 			return;				/* nothing to do; the FSM was already smaller */
  	}
  
! 	/* Truncate the unused FSM pages, and send smgr inval message */
! 	smgrtruncate(rel->rd_smgr, FSM_FORKNUM, new_nfsmblocks);
! 
! 	/*
! 	 * We might as well update the local smgr_fsm_nblocks setting.
! 	 * smgrtruncate sent an smgr cache inval message, which will cause other
! 	 * backends to invalidate their copy of smgr_fsm_nblocks, and this one too
! 	 * at the next command boundary.  But this ensures it isn't outright wrong
! 	 * until then.
! 	 */
! 	if (rel->rd_smgr)
! 		rel->rd_smgr->smgr_fsm_nblocks = new_nfsmblocks;
! 
! 	/*
! 	 * Update upper-level FSM pages to account for the truncation.  This is
! 	 * important because the just-truncated pages were likely marked as
! 	 * all-free, and would be preferentially selected.
! 	 */
! 	FreeSpaceMapVacuumRange(rel, nblocks, InvalidBlockNumber);
  }
  
  /*
--- 317,326 ----
  	{
  		new_nfsmblocks = fsm_logical_to_physical(first_removed_address);
  		if (smgrnblocks(rel->rd_smgr, FSM_FORKNUM) <= new_nfsmblocks)
! 			return InvalidBlockNumber;	/* nothing to do; the FSM was already smaller */
  	}
  
! 	return new_nfsmblocks;
  }
  
  /*
diff --git a/src/backend/storage/smgr/smgr.c b/sindex 5b5a80e890..b50c69b438 100644
*** a/src/backend/storage/smgr/smgr.c
--- b/src/backend/storage/smgr/smgr.c
***************
*** 469,474 **** smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
--- 469,475 ----
  	pfree(rnodes);
  }
  
+ 
  /*
   *	smgrextend() -- Add a new block to a file.
   *
***************
*** 557,575 **** smgrnblocks(SMgrRelation reln, ForkNumber forknum)
  }
  
  /*
!  *	smgrtruncate() -- Truncate supplied relation to the specified number
!  *					  of blocks
   *
   * The truncation is done immediately, so this can't be rolled back.
   */
  void
! smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
  {
  	/*
  	 * Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
  	 * just drop them without bothering to write the contents.
  	 */
! 	DropRelFileNodeBuffers(reln->smgr_rnode, forknum, nblocks);
  
  	/*
  	 * Send a shared-inval message to force other backends to close any smgr
--- 558,582 ----
  }
  
  /*
!  *	smgrtruncate() -- Truncate the given forks of supplied relation to
!  *					  each specified numbers of blocks
   *
   * The truncation is done immediately, so this can't be rolled back.
+  *
+  * The caller must hold AccessExclusiveLock on the relation, to ensure that
+  * other backends receive the smgr invalidation event that this function sends
+  * before they access any forks of the relation again.
   */
  void
! smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nblocks)
  {
+ 	int		i;
+ 
  	/*
  	 * Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
  	 * just drop them without bothering to write the contents.
  	 */
! 	DropRelFileNodeBuffers(reln->smgr_rnode, forknum, nforks, nblocks);
  
  	/*
  	 * Send a shared-inval message to force other backends to close any smgr
***************
*** 583,592 **** smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
  	 */
  	CacheInvalidateSmgr(reln->smgr_rnode);
  
! 	/*
! 	 * Do the truncation.
! 	 */
! 	smgrsw[reln->smgr_which].smgr_truncate(reln, forknum, nblocks);
  }
  
  /*
--- 590,613 ----
  	 */
  	CacheInvalidateSmgr(reln->smgr_rnode);
  
! 	/* Do the truncation */
! 	for (i = 0; i < nforks; i++)
! 	{
! 		smgrsw[reln->smgr_which].smgr_truncate(reln, forknum[i], nblocks[i]);
! 
! 		/*
! 		 * We might as well update the local smgr_fsm_nblocks and
! 		 * smgr_vm_nblocks settings. The smgr cache inval message that
! 		 * this function sent will cause other backends to invalidate
! 		 * their copies of smgr_fsm_nblocks and smgr_vm_nblocks,
! 		 * and these ones too at the next command boundary.
! 		 * But these ensure they aren't outright wrong until then.
! 		 */
! 		if (forknum[i] == FSM_FORKNUM)
! 			reln->smgr_fsm_nblocks = nblocks[i];
! 		if (forknum[i] == VISIBILITYMAP_FORKNUM)
! 			reln->smgr_vm_nblocks = nblocks[i];
! 	}
  }
  
  /*
diff --git a/src/include/access/visibiindex 2d8804351a..0532b04e34 100644
*** a/src/include/access/visibilitymap.h
--- b/src/include/access/visibilitymap.h
***************
*** 44,49 **** extern void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
  							  uint8 flags);
  extern uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf);
  extern void visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen);
! extern void visibilitymap_truncate(Relation rel, BlockNumber nheapblocks);
  
  #endif							/* VISIBILITYMAP_H */
--- 44,50 ----
  							  uint8 flags);
  extern uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf);
  extern void visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen);
! extern BlockNumber visibilitymap_prepare_truncate(Relation rel,
! 							  BlockNumber nheapblocks);
  
  #endif							/* VISIBILITYMAP_H */
diff --git a/src/include/storage/bufmgr.hindex 509f4b7ef1..17b97f7e38 100644
*** a/src/include/storage/bufmgr.h
--- b/src/include/storage/bufmgr.h
***************
*** 190,197 **** extern BlockNumber RelationGetNumberOfBlocksInFork(Relation relation,
  extern void FlushOneBuffer(Buffer buffer);
  extern void FlushRelationBuffers(Relation rel);
  extern void FlushDatabaseBuffers(Oid dbid);
! extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,
! 								   ForkNumber forkNum, BlockNumber firstDelBlock);
  extern void DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes);
  extern void DropDatabaseBuffers(Oid dbid);
  
--- 190,197 ----
  extern void FlushOneBuffer(Buffer buffer);
  extern void FlushRelationBuffers(Relation rel);
  extern void FlushDatabaseBuffers(Oid dbid);
! extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber *forkNum,
! 								   int nforks, BlockNumber *firstDelBlock);
  extern void DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes);
  extern void DropDatabaseBuffers(Oid dbid);
  
diff --git a/src/include/storage/frindex 8d8c465d7b..b75f6fe946 100644
*** a/src/include/storage/freespace.h
--- b/src/include/storage/freespace.h
***************
*** 30,36 **** extern void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk,
  extern void XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk,
  										Size spaceAvail);
  
! extern void FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks);
  extern void FreeSpaceMapVacuum(Relation rel);
  extern void FreeSpaceMapVacuumRange(Relation rel, BlockNumber start,
  									BlockNumber end);
--- 30,37 ----
  extern void XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk,
  										Size spaceAvail);
  
! extern BlockNumber FreeSpaceMapPrepareTruncateRel(Relation rel,
! 												  BlockNumber nblocks);
  extern void FreeSpaceMapVacuum(Relation rel);
  extern void FreeSpaceMapVacuumRange(Relation rel, BlockNumber start,
  									BlockNumber end);
diff --git a/src/include/storage/smgr.index 7393727a4b..1543d8d870 100644
*** a/src/include/storage/smgr.h
--- b/src/include/storage/smgr.h
***************
*** 101,108 **** extern void smgrwrite(SMgrRelation reln, ForkNumber forknum,
  extern void smgrwriteback(SMgrRelation reln, ForkNumber forknum,
  						  BlockNumber blocknum, BlockNumber nblocks);
  extern BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum);
! extern void smgrtruncate(SMgrRelation reln, ForkNumber forknum,
! 						 BlockNumber nblocks);
  extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum);
  extern void AtEOXact_SMgr(void);
  
--- 101,108 ----
  extern void smgrwriteback(SMgrRelation reln, ForkNumber forknum,
  						  BlockNumber blocknum, BlockNumber nblocks);
  extern BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum);
! extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum,
! 						 int nforks, BlockNumber *nblocks);
  extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum);
  extern void AtEOXact_SMgr(void);
  
