diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index c20b2ebfe1bc6081ef4c0d549a5f33ba99ea1ffc..8a2e7929e621edfcc8d854c29cce8b834d47e0e5 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.14 2007/01/31 20:56:19 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.15 2007/03/02 00:48:44 tgl Exp $ -->
 
 <chapter id="storage">
 
@@ -427,8 +427,8 @@ data. Empty in ordinary tables.</entry>
   The first 20 bytes of each page consists of a page header
   (PageHeaderData). Its format is detailed in <xref
   linkend="pageheaderdata-table">. The first two fields track the most
-  recent WAL entry related to this page. They are followed by three 2-byte
-  integer fields
+  recent WAL entry related to this page. Next is a 2-byte field
+  containing flag bits. This is followed by three 2-byte integer fields
   (<structfield>pd_lower</structfield>, <structfield>pd_upper</structfield>,
   and <structfield>pd_special</structfield>). These contain byte offsets
   from the page start to the start
@@ -437,12 +437,13 @@ data. Empty in ordinary tables.</entry>
   The last 2 bytes of the page header,
   <structfield>pd_pagesize_version</structfield>, store both the page size
   and a version indicator.  Beginning with
-  <productname>PostgreSQL</productname> 8.1 the version number is 3;
+  <productname>PostgreSQL</productname> 8.3 the version number is 4;
+  <productname>PostgreSQL</productname> 8.1 and 8.2 used version number 3;
   <productname>PostgreSQL</productname> 8.0 used version number 2;
   <productname>PostgreSQL</productname> 7.3 and 7.4 used version number 1;
   prior releases used version number 0.
-  (The basic page layout and header format has not changed in these versions,
-  but the layout of heap row headers has.)  The page size
+  (The basic page layout and header format has not changed in most of these
+  versions, but the layout of heap row headers has.)  The page size
   is basically only present as a cross-check; there is no support for having
   more than one page size in an installation.
   
@@ -470,9 +471,15 @@ data. Empty in ordinary tables.</entry>
   </row>
   <row>
    <entry>pd_tli</entry>
-   <entry>TimeLineID</entry>
-   <entry>4 bytes</entry>
-   <entry>TLI of last change</entry>
+   <entry>uint16</entry>
+   <entry>2 bytes</entry>
+   <entry>TimeLineID of last change (only its lowest 16 bits)</entry>
+  </row>
+  <row>
+   <entry>pd_flags</entry>
+   <entry>uint16</entry>
+   <entry>2 bytes</entry>
+   <entry>Flag bits</entry>
   </row>
   <row>
    <entry>pd_lower</entry>
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 8299f550d6e02303db13c289565832367003758e..b246b0afeb87bc2a053723c7e8e0fe8a68356411 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.71 2007/02/21 20:02:17 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.72 2007/03/02 00:48:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,6 +39,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* Make sure all fields of page are zero, as well as unused space */
 	MemSet(p, 0, pageSize);
 
+	/* p->pd_flags = 0; 					done by above MemSet */
 	p->pd_lower = SizeOfPageHeaderData;
 	p->pd_upper = pageSize - specialSize;
 	p->pd_special = pageSize - specialSize;
@@ -73,6 +74,7 @@ PageHeaderIsValid(PageHeader page)
 	/* Check normal case */
 	if (PageGetPageSize(page) == BLCKSZ &&
 		PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
+		(page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
 		page->pd_lower >= SizeOfPageHeaderData &&
 		page->pd_lower <= page->pd_upper &&
 		page->pd_upper <= page->pd_special &&
@@ -165,14 +167,27 @@ PageAddItem(Page page,
 	else
 	{
 		/* offsetNumber was not passed in, so find a free slot */
-		/* look for "recyclable" (unused & deallocated) ItemId */
-		for (offsetNumber = 1; offsetNumber < limit; offsetNumber++)
+		/* if no free slot, we'll put it at limit (1st open slot) */
+		if (PageHasFreeLinePointers(phdr))
+		{
+			/* look for "recyclable" (unused & deallocated) ItemId */
+			for (offsetNumber = 1; offsetNumber < limit; offsetNumber++)
+			{
+				itemId = PageGetItemId(phdr, offsetNumber);
+				if (!ItemIdIsUsed(itemId) && ItemIdGetLength(itemId) == 0)
+					break;
+			}
+			if (offsetNumber >= limit)
+			{
+				/* the hint is wrong, so reset it */
+				PageClearHasFreeLinePointers(phdr);
+			}
+		}
+		else
 		{
-			itemId = PageGetItemId(phdr, offsetNumber);
-			if (!ItemIdIsUsed(itemId) && ItemIdGetLength(itemId) == 0)
-				break;
+			/* don't bother searching if hint says there's no free slot */
+			offsetNumber = limit;
 		}
-		/* if no free slot, we'll put it at limit (1st open slot) */
 	}
 
 	if (offsetNumber > limit)
@@ -413,13 +428,19 @@ PageRepairFragmentation(Page page, OffsetNumber *unused)
 		pfree(itemidbase);
 	}
 
+	/* Set hint bit for PageAddItem */
+	if (nused < nline)
+		PageSetHasFreeLinePointers(page);
+	else
+		PageClearHasFreeLinePointers(page);
+
 	return (nline - nused);
 }
 
 /*
  * PageGetFreeSpace
  *		Returns the size of the free (allocatable) space on a page,
- *		deducted by the space needed for a new line pointer.
+ *		reduced by the space needed for a new line pointer.
  */
 Size
 PageGetFreeSpace(Page page)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 8c53a2df2ac7be441278ef63288094beb31cdacc..51263557f9ef73b9ecafbab03b990604e7a812e9 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.388 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.389 2007/03/02 00:48:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200702202
+#define CATALOG_VERSION_NO	200703011
 
 #endif
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 44ab48c9b8f6d8daf33630067febcb3bf246c6bd..d38544e3f0570b51310a95219db95a9ee0cc1e73 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.71 2007/02/21 20:02:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.72 2007/03/02 00:48:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,6 +90,7 @@ typedef uint16 LocationIndex;
  *
  *		pd_lsn		- identifies xlog record for last change to this page.
  *		pd_tli		- ditto.
+ *		pd_flags	- flag bits.
  *		pd_lower	- offset to start of free space.
  *		pd_upper	- offset to end of free space.
  *		pd_special	- offset to start of special space.
@@ -98,8 +99,9 @@ typedef uint16 LocationIndex;
  * The LSN is used by the buffer manager to enforce the basic rule of WAL:
  * "thou shalt write xlog before data".  A dirty buffer cannot be dumped
  * to disk until xlog has been flushed at least as far as the page's LSN.
- * We also store the TLI for identification purposes (it is not clear that
- * this is actually necessary, but it seems like a good idea).
+ * We also store the 16 least significant bits of the TLI for identification
+ * purposes (it is not clear that this is actually necessary, but it seems
+ * like a good idea).
  *
  * The page version number and page size are packed together into a single
  * uint16 field.  This is for historical reasons: before PostgreSQL 7.3,
@@ -119,7 +121,9 @@ typedef struct PageHeaderData
 	/* XXX LSN is member of *any* block, not only page-organized ones */
 	XLogRecPtr	pd_lsn;			/* LSN: next byte after last byte of xlog
 								 * record for last change to this page */
-	TimeLineID	pd_tli;			/* TLI of last change */
+	uint16		pd_tli;			/* least significant bits of the TimeLineID
+								 * containing the LSN */
+	uint16		pd_flags;		/* flag bits, see below */
 	LocationIndex pd_lower;		/* offset to start of free space */
 	LocationIndex pd_upper;		/* offset to end of free space */
 	LocationIndex pd_special;	/* offset to start of special space */
@@ -129,12 +133,25 @@ typedef struct PageHeaderData
 
 typedef PageHeaderData *PageHeader;
 
+/*
+ * pd_flags contains the following flag bits.  Undefined bits are initialized
+ * to zero and may be used in the future.
+ *
+ * PD_HAS_FREE_LINES is set if there are any not-LP_USED line pointers before
+ * pd_lower.  This should be considered a hint rather than the truth, since
+ * changes to it are not WAL-logged.
+ */
+#define PD_HAS_FREE_LINES	0x0001	/* are there any unused line pointers? */
+
+#define PD_VALID_FLAG_BITS	0x0001	/* OR of all valid pd_flags bits */
+
 /*
  * Page layout version number 0 is for pre-7.3 Postgres releases.
  * Releases 7.3 and 7.4 use 1, denoting a new HeapTupleHeader layout.
  * Release 8.0 uses 2; it changed the HeapTupleHeader layout again.
  * Release 8.1 uses 3; it redefined HeapTupleHeader infomask bits.
- * Release 8.3 uses 4; it changed the HeapTupleHeader layout again.
+ * Release 8.3 uses 4; it changed the HeapTupleHeader layout again, and
+ * added the pd_flags field (by stealing some bits from pd_tli).
  */
 #define PG_PAGE_LAYOUT_VERSION		4
 
@@ -299,15 +316,27 @@ typedef PageHeaderData *PageHeader;
 	 ((((PageHeader) (page))->pd_lower - SizeOfPageHeaderData) \
 	  / sizeof(ItemIdData)))
 
+/*
+ * Additional macros for access to page headers
+ */
 #define PageGetLSN(page) \
 	(((PageHeader) (page))->pd_lsn)
 #define PageSetLSN(page, lsn) \
 	(((PageHeader) (page))->pd_lsn = (lsn))
 
+/* NOTE: only the 16 least significant bits are stored */
 #define PageGetTLI(page) \
 	(((PageHeader) (page))->pd_tli)
 #define PageSetTLI(page, tli) \
-	(((PageHeader) (page))->pd_tli = (tli))
+	(((PageHeader) (page))->pd_tli = (uint16) (tli))
+
+#define PageHasFreeLinePointers(page) \
+	(((PageHeader) (page))->pd_flags & PD_HAS_FREE_LINES)
+#define PageSetHasFreeLinePointers(page) \
+	(((PageHeader) (page))->pd_flags |= PD_HAS_FREE_LINES)
+#define PageClearHasFreeLinePointers(page) \
+	(((PageHeader) (page))->pd_flags &= ~PD_HAS_FREE_LINES)
+
 
 /* ----------------------------------------------------------------
  *		extern declarations