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