diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 99235da..e110a66 100644
*** a/src/backend/access/heap/rewriteheap.c
--- b/src/backend/access/heap/rewriteheap.c
*************** rewrite_heap_tuple(RewriteState state,
*** 501,509 ****
  /*
   * Register a dead tuple with an ongoing rewrite. Dead tuples are not
   * copied to the new table, but we still make note of them so that we
!  * can release some resources earlier.
   */
! void
  rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
  {
  	/*
--- 501,509 ----
  /*
   * Register a dead tuple with an ongoing rewrite. Dead tuples are not
   * copied to the new table, but we still make note of them so that we
!  * can release some resources earlier. Returns true if actually registered.
   */
! bool
  rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
  {
  	/*
*************** rewrite_heap_dead_tuple(RewriteState sta
*** 539,545 ****
--- 539,549 ----
  		hash_search(state->rs_unresolved_tups, &hashkey,
  					HASH_REMOVE, &found);
  		Assert(found);
+ 
+ 		return true;
  	}
+ 
+ 	return false;
  }
  
  /*
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 383d7cf..2041a85 100644
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 45,50 ****
--- 45,51 ----
  #include "utils/inval.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
+ #include "utils/pg_rusage.h"
  #include "utils/relcache.h"
  #include "utils/relmapper.h"
  #include "utils/snapmgr.h"
*************** typedef struct
*** 66,75 ****
  
  
  static void rebuild_relation(Relation OldHeap, Oid indexOid,
! 				 int freeze_min_age, int freeze_table_age);
  static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
  			   int freeze_min_age, int freeze_table_age,
! 			   bool *pSwapToastByContent, TransactionId *pFreezeXid);
  static List *get_tables_to_cluster(MemoryContext cluster_context);
  static void deform_and_rewrite_tuple(HeapTuple tuple, TupleDesc oldTupDesc,
  			   TupleDesc newTupDesc, Datum *values, bool *isnull,
--- 67,77 ----
  
  
  static void rebuild_relation(Relation OldHeap, Oid indexOid,
! 				 int freeze_min_age, int freeze_table_age, bool verbose);
  static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
  			   int freeze_min_age, int freeze_table_age,
! 			   bool *pSwapToastByContent, TransactionId *pFreezeXid,
! 			   bool verbose);
  static List *get_tables_to_cluster(MemoryContext cluster_context);
  static void deform_and_rewrite_tuple(HeapTuple tuple, TupleDesc oldTupDesc,
  			   TupleDesc newTupDesc, Datum *values, bool *isnull,
*************** cluster_rel(Oid tableOid, Oid indexOid, 
*** 383,402 ****
  	if (OidIsValid(indexOid))
  		check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
  
- 	/* Log what we're doing (this could use more effort) */
- 	if (OidIsValid(indexOid))
- 		ereport(verbose ? INFO : DEBUG2,
- 				(errmsg("clustering \"%s.%s\"",
- 						get_namespace_name(RelationGetNamespace(OldHeap)),
- 						RelationGetRelationName(OldHeap))));
- 	else
- 		ereport(verbose ? INFO : DEBUG2,
- 				(errmsg("vacuuming \"%s.%s\"",
- 						get_namespace_name(RelationGetNamespace(OldHeap)),
- 						RelationGetRelationName(OldHeap))));
- 
  	/* rebuild_relation does all the dirty work */
! 	rebuild_relation(OldHeap, indexOid, freeze_min_age, freeze_table_age);
  
  	/* NB: rebuild_relation does heap_close() on OldHeap */
  }
--- 385,392 ----
  	if (OidIsValid(indexOid))
  		check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
  
  	/* rebuild_relation does all the dirty work */
! 	rebuild_relation(OldHeap, indexOid, freeze_min_age, freeze_table_age, verbose);
  
  	/* NB: rebuild_relation does heap_close() on OldHeap */
  }
*************** mark_index_clustered(Relation rel, Oid i
*** 580,586 ****
   */
  static void
  rebuild_relation(Relation OldHeap, Oid indexOid,
! 				 int freeze_min_age, int freeze_table_age)
  {
  	Oid			tableOid = RelationGetRelid(OldHeap);
  	Oid			tableSpace = OldHeap->rd_rel->reltablespace;
--- 570,576 ----
   */
  static void
  rebuild_relation(Relation OldHeap, Oid indexOid,
! 				 int freeze_min_age, int freeze_table_age, bool verbose)
  {
  	Oid			tableOid = RelationGetRelid(OldHeap);
  	Oid			tableSpace = OldHeap->rd_rel->reltablespace;
*************** rebuild_relation(Relation OldHeap, Oid i
*** 605,611 ****
  	/* Copy the heap data into the new table in the desired order */
  	copy_heap_data(OIDNewHeap, tableOid, indexOid,
  				   freeze_min_age, freeze_table_age,
! 				   &swap_toast_by_content, &frozenXid);
  
  	/*
  	 * Swap the physical files of the target and transient tables, then
--- 595,601 ----
  	/* Copy the heap data into the new table in the desired order */
  	copy_heap_data(OIDNewHeap, tableOid, indexOid,
  				   freeze_min_age, freeze_table_age,
! 				   &swap_toast_by_content, &frozenXid, verbose);
  
  	/*
  	 * Swap the physical files of the target and transient tables, then
*************** make_new_heap(Oid OIDOldHeap, Oid NewTab
*** 747,753 ****
  static void
  copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
  			   int freeze_min_age, int freeze_table_age,
! 			   bool *pSwapToastByContent, TransactionId *pFreezeXid)
  {
  	Relation	NewHeap,
  				OldHeap,
--- 737,744 ----
  static void
  copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
  			   int freeze_min_age, int freeze_table_age,
! 			   bool *pSwapToastByContent, TransactionId *pFreezeXid,
! 			   bool verbose)
  {
  	Relation	NewHeap,
  				OldHeap,
*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl
*** 766,771 ****
--- 757,769 ----
  	RewriteState rwstate;
  	bool 		 use_sort;
  	Tuplesortstate *tuplesort;
+ 	PGRUsage	ru0;
+ 	double		num_tuples = 0,
+ 				tups_vacuumed = 0,
+ 				nkeep = 0;
+ 	int			elevel = verbose ? INFO : DEBUG2;
+ 
+ 	pg_rusage_init(&ru0);
  
  	/*
  	 * Open the relations we need.
*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl
*** 891,896 ****
--- 889,912 ----
  			tuplesort = NULL;
  	}
  
+ 	/* Log what we're doing */
+ 	if (indexScan != NULL)
+ 		ereport(elevel,
+ 				(errmsg("clustering \"%s.%s\" by index scan on \"%s\"",
+ 						get_namespace_name(RelationGetNamespace(OldHeap)),
+ 						RelationGetRelationName(OldHeap),
+ 						RelationGetRelationName(OldIndex))));
+ 	else if (tuplesort != NULL)
+ 		ereport(elevel,
+ 				(errmsg("clustering \"%s.%s\" by sequential scan and sort",
+ 						get_namespace_name(RelationGetNamespace(OldHeap)),
+ 						RelationGetRelationName(OldHeap))));
+ 	else
+ 		ereport(elevel,
+ 				(errmsg("vacuuming \"%s.%s\"",
+ 						get_namespace_name(RelationGetNamespace(OldHeap)),
+ 						RelationGetRelationName(OldHeap))));
+ 
  	for (;;)
  	{
  		HeapTuple	tuple;
*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl
*** 928,935 ****
  				/* Definitely dead */
  				isdead = true;
  				break;
- 			case HEAPTUPLE_LIVE:
  			case HEAPTUPLE_RECENTLY_DEAD:
  				/* Live or recently dead, must copy it */
  				isdead = false;
  				break;
--- 944,953 ----
  				/* Definitely dead */
  				isdead = true;
  				break;
  			case HEAPTUPLE_RECENTLY_DEAD:
+ 				nkeep += 1;
+ 				/* fall through */
+ 			case HEAPTUPLE_LIVE:
  				/* Live or recently dead, must copy it */
  				isdead = false;
  				break;
*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl
*** 973,983 ****
  
  		if (isdead)
  		{
  			/* heap rewrite module still needs to see it... */
! 			rewrite_heap_dead_tuple(rwstate, tuple);
  			continue;
  		}
  
  		if (tuplesort != NULL)
  			tuplesort_putrawtuple(tuplesort, tuple);
  		else
--- 991,1004 ----
  
  		if (isdead)
  		{
+ 			tups_vacuumed += 1;
  			/* heap rewrite module still needs to see it... */
! 			if (rewrite_heap_dead_tuple(rwstate, tuple))
! 				nkeep += 1;
  			continue;
  		}
  
+ 		num_tuples += 1;
  		if (tuplesort != NULL)
  			tuplesort_putrawtuple(tuplesort, tuple);
  		else
*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl
*** 1030,1035 ****
--- 1051,1065 ----
  	pfree(values);
  	pfree(isnull);
  
+ 	ereport(elevel,
+ 			(errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
+ 					RelationGetRelationName(OldHeap),
+ 					tups_vacuumed, num_tuples, RelationGetNumberOfBlocks(OldHeap)),
+ 			 errdetail("%.0f dead row versions cannot be removed yet.\n"
+ 					   "%s.",
+ 					   nkeep,
+ 					   pg_rusage_show(&ru0))));
+ 
  	if (OldIndex != NULL)
  		index_close(OldIndex, NoLock);
  	heap_close(OldHeap, NoLock);
diff --git a/src/include/access/rewriteheap.h b/src/include/access/rewriteheap.h
index 8ede6be..53a9ded 100644
*** a/src/include/access/rewriteheap.h
--- b/src/include/access/rewriteheap.h
*************** extern RewriteState begin_heap_rewrite(R
*** 25,30 ****
  extern void end_heap_rewrite(RewriteState state);
  extern void rewrite_heap_tuple(RewriteState state, HeapTuple oldTuple,
  				   HeapTuple newTuple);
! extern void rewrite_heap_dead_tuple(RewriteState state, HeapTuple oldTuple);
  
  #endif   /* REWRITE_HEAP_H */
--- 25,30 ----
  extern void end_heap_rewrite(RewriteState state);
  extern void rewrite_heap_tuple(RewriteState state, HeapTuple oldTuple,
  				   HeapTuple newTuple);
! extern bool rewrite_heap_dead_tuple(RewriteState state, HeapTuple oldTuple);
  
  #endif   /* REWRITE_HEAP_H */