Suppress useless searches for unused line pointers in PageAddItem. To do
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Mar 2007 00:48:44 +0000 (00:48 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Mar 2007 00:48:44 +0000 (00:48 +0000)
this, add a 16-bit "flags" field to page headers by stealing some bits from
pd_tli.  We use one flag bit as a hint to indicate whether there are any
unused line pointers; the remaining 15 are available for future use.

This is a cut-down form of an idea proposed by Hiroki Kataoka in July 2005.
At the time it was rejected because the original patch increased the size of
page headers and it wasn't clear that the benefit outweighed the distributed
cost.  The flag-bit approach gets most of the benefit without requiring an
increase in the page header size.

Heikki Linnakangas and Tom Lane

doc/src/sgml/storage.sgml
src/backend/storage/page/bufpage.c
src/include/catalog/catversion.h
src/include/storage/bufpage.h

index f1beb96d2e77ceaaac08f1fde12d14c46b18b6e2..0baa32fd389523f4519804c3d2e4920de9f19a4c 100644 (file)
@@ -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>
index 5ac7e2c60a74da06767a2fbafc239db339fc51b3..b63a19182957054863e512e999bc790ac1188b24 100644 (file)
@@ -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)
index 07888caf876b03030e61062c7f7525e4bb942059..267d964cc2575e0ed20320b09d250630e8e613cd 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200702202
+#define CATALOG_VERSION_NO     200703011
 
 #endif
index 035fd872deaa290ef9e3e71f0fef77c7c7725687..deb62477147c2478edd5e9f2f4e293d2525a3d32 100644 (file)
@@ -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