diff --git a/contrib/pgstattuple/README.pgstattuple b/contrib/pgstattuple/README.pgstattuple
index c54f97e82f5ba731dcb077113195672a4f9659d6..6a191e9cced316a92f532536d74b67f21cd34a81 100644
--- a/contrib/pgstattuple/README.pgstattuple
+++ b/contrib/pgstattuple/README.pgstattuple
@@ -15,7 +15,7 @@ NOTICE:  physical length: 0.08MB live tuples: 20 (0.00MB, 1.17%) dead tuples: 32
        18.75
 (1 row)
 
-   Above example shows tellers tables includes 18.75% dead tuples.
+   Above example shows tellers table includes 18.75% dead tuples.
 
    physical length	physical size of the table in MB
    live tuples		information on the live tuples
@@ -40,7 +40,7 @@ NOTICE:  physical length: 0.08MB live tuples: 20 (0.00MB, 1.17%) dead tuples: 32
 
 4. Notes
 
-   pgstattuple does not lock the target table at all. So concurrent
+   pgstattuple acquires only a read lock on the table. So concurrent
    update may affect the result.
 
    pgstattuple judges a tuple is "dead" if HeapTupleSatisfiesNow()
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 45e92298100b96dbdcf2cb434a4e9515ec9baa69..b5afab80de1ec4c62968b371f73ed2b8de833dcd 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -1,5 +1,5 @@
 /*
- * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.2 2001/10/25 05:49:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.3 2001/12/19 20:28:41 tgl Exp $
  *
  * Copyright (c) 2001  Tatsuo Ishii
  *
@@ -23,6 +23,7 @@
  */
 
 #include "postgres.h"
+
 #include "fmgr.h"
 #include "access/heapam.h"
 #include "access/transam.h"
@@ -48,20 +49,21 @@ pgstattuple(PG_FUNCTION_ARGS)
 	HeapScanDesc scan;
 	HeapTuple	tuple;
 	BlockNumber nblocks;
-	BlockNumber block = InvalidBlockNumber;
+	BlockNumber block = 0;		/* next block to count free space in */
+	BlockNumber tupblock;
+	Buffer		buffer;
 	double		table_len;
 	uint64		tuple_len = 0;
 	uint64		dead_tuple_len = 0;
-	uint32		tuple_count = 0;
-	uint32		dead_tuple_count = 0;
+	uint64		tuple_count = 0;
+	uint64		dead_tuple_count = 0;
 	double		tuple_percent;
 	double		dead_tuple_percent;
 
-	Buffer		buffer = InvalidBuffer;
 	uint64		free_space = 0; /* free/reusable space in bytes */
 	double		free_percent;	/* free/reusable space in % */
 
-	rel = heap_openr(NameStr(*p), NoLock);
+	rel = heap_openr(NameStr(*p), AccessShareLock);
 	nblocks = RelationGetNumberOfBlocks(rel);
 	scan = heap_beginscan(rel, false, SnapshotAny, 0, NULL);
 
@@ -78,17 +80,33 @@ pgstattuple(PG_FUNCTION_ARGS)
 			dead_tuple_count++;
 		}
 
-		if (!BlockNumberIsValid(block) ||
-			block != BlockIdGetBlockNumber(&tuple->t_self.ip_blkid))
+		/*
+		 * To avoid physically reading the table twice, try to do the
+		 * free-space scan in parallel with the heap scan.  However,
+		 * heap_getnext may find no tuples on a given page, so we cannot
+		 * simply examine the pages returned by the heap scan.
+		 */
+		tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid);
+
+		while (block <= tupblock)
 		{
-			block = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid);
 			buffer = ReadBuffer(rel, block);
 			free_space += PageGetFreeSpace((Page) BufferGetPage(buffer));
 			ReleaseBuffer(buffer);
+			block++;
 		}
 	}
 	heap_endscan(scan);
-	heap_close(rel, NoLock);
+
+	while (block < nblocks)
+	{
+		buffer = ReadBuffer(rel, block);
+		free_space += PageGetFreeSpace((Page) BufferGetPage(buffer));
+		ReleaseBuffer(buffer);
+		block++;
+	}
+
+	heap_close(rel, AccessShareLock);
 
 	table_len = (double) nblocks *BLCKSZ;
 
@@ -105,20 +123,20 @@ pgstattuple(PG_FUNCTION_ARGS)
 		free_percent = (double) free_space *100.0 / table_len;
 	}
 
-	elog(NOTICE, "physical length: %.2fMB live tuples: %u (%.2fMB, %.2f%%) dead tuples: %u (%.2fMB, %.2f%%) free/reusable space: %.2fMB (%.2f%%) overhead: %.2f%%",
+	elog(NOTICE, "physical length: %.2fMB live tuples: %.0f (%.2fMB, %.2f%%) dead tuples: %.0f (%.2fMB, %.2f%%) free/reusable space: %.2fMB (%.2f%%) overhead: %.2f%%",
 
-		 table_len / 1024 / 1024,		/* phsical length in MB */
+		 table_len / (1024 * 1024),		/* physical length in MB */
 
-		 tuple_count,			/* number of live tuples */
-		 (double) tuple_len / 1024 / 1024,		/* live tuples in MB */
+		 (double) tuple_count,	/* number of live tuples */
+		 (double) tuple_len / (1024 * 1024),		/* live tuples in MB */
 		 tuple_percent,			/* live tuples in % */
 
-		 dead_tuple_count,		/* number of dead tuples */
-		 (double) dead_tuple_len / 1024 / 1024, /* dead tuples in MB */
+		 (double) dead_tuple_count,	/* number of dead tuples */
+		 (double) dead_tuple_len / (1024 * 1024), /* dead tuples in MB */
 		 dead_tuple_percent,	/* dead tuples in % */
 
-		 (double) free_space / 1024 / 1024,		/* free/available space in
-												 * MB */
+		 (double) free_space / (1024 * 1024), /* free/available space in
+											   * MB */
 
 		 free_percent,			/* free/available space in % */