{"Entry":{"collection":"storage","key":"storage-page-layout","name":"Database Page Layout","aliases":[],"metadata":{"aliases":[],"category":"Physical structures","content_hash":"5fec875e92723bd75c390ddb5c9c10c11fdfd958569d40b8f06ed2fea9768d70","imported_at":"2026-09-30T00:40:47.078444+08:00","name":"Database Page Layout","name_zh":"","slug":"storage-page-layout","summary":"This section provides an overview of the page format used within PostgreSQL tables and indexes. [20] Sequences and TOAST tables are formatted just like a regular table."}},"Definition":{"Collection":"storage","Key":"storage-page-layout","SourceDatabase":"center","Version":"18","SourceTable":"storage_structure","SourceKey":"storage-page-layout","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","Facts":{"comparison_data":{"layouts":[{"columns":[{"key":"c0","label":"Item"},{"key":"c1","label":"Description"}],"key":"layout-0","rows":[{"c0":"PageHeaderData","c1":"24 bytes long. Contains general information about the page, including free space pointers."},{"c0":"ItemIdData","c1":"Array of item identifiers pointing to the actual items. Each entry is an (offset,length) pair. 4 bytes per item."},{"c0":"Free space","c1":"The unallocated space. New item identifiers are allocated from the start of this area, new items from the end."},{"c0":"Items","c1":"The actual items themselves."},{"c0":"Special space","c1":"Index access method specific data. Different methods store different data. Empty in ordinary tables."}],"title":"Overall Page Layout"},{"columns":[{"key":"c0","label":"Field"},{"key":"c1","label":"Type"},{"key":"c2","label":"Length"},{"key":"c3","label":"Description"}],"key":"layout-1","rows":[{"c0":"pd_lsn","c1":"PageXLogRecPtr","c2":"8 bytes","c3":"LSN: next byte after last byte of WAL record for last change to this page"},{"c0":"pd_checksum","c1":"uint16","c2":"2 bytes","c3":"Page checksum"},{"c0":"pd_flags","c1":"uint16","c2":"2 bytes","c3":"Flag bits"},{"c0":"pd_lower","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to start of free space"},{"c0":"pd_upper","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to end of free space"},{"c0":"pd_special","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to start of special space"},{"c0":"pd_pagesize_version","c1":"uint16","c2":"2 bytes","c3":"Page size and layout version number information"},{"c0":"pd_prune_xid","c1":"TransactionId","c2":"4 bytes","c3":"Oldest unpruned XMAX on page, or zero if none"}],"title":"PageHeaderData Layout"},{"columns":[{"key":"c0","label":"Field"},{"key":"c1","label":"Type"},{"key":"c2","label":"Length"},{"key":"c3","label":"Description"}],"key":"layout-2","rows":[{"c0":"t_xmin","c1":"TransactionId","c2":"4 bytes","c3":"insert XID stamp"},{"c0":"t_xmax","c1":"TransactionId","c2":"4 bytes","c3":"delete XID stamp"},{"c0":"t_cid","c1":"CommandId","c2":"4 bytes","c3":"insert and/or delete CID stamp (overlays with t_xvac)"},{"c0":"t_xvac","c1":"TransactionId","c2":"4 bytes","c3":"XID for VACUUM operation moving a row version"},{"c0":"t_ctid","c1":"ItemPointerData","c2":"6 bytes","c3":"current TID of this or newer row version"},{"c0":"t_infomask2","c1":"uint16","c2":"2 bytes","c3":"number of attributes, plus various flag bits"},{"c0":"t_infomask","c1":"uint16","c2":"2 bytes","c3":"various flag bits"},{"c0":"t_hoff","c1":"uint8","c2":"1 byte","c3":"offset to user data"}],"title":"HeapTupleHeaderData Layout"}]},"comparison_hash":"b621fa85b3be27a0eb0d82986e9524f2ef0a776761901f0d429826eec4822d0a","description":["This section provides an overview of the page format used within PostgreSQL tables and indexes. [19] Sequences and TOAST tables are formatted just like a regular table."],"diagram":"page-layout","facts":[{"label":"Definition scope","value":"Same-version core physical storage documentation"}],"manual_html":"\u003cdiv class=\"sect1\"\u003e\n\u003cdiv class=\"titlepage\"\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch2 class=\"title\"\u003e66.6. Database Page Layout \u003c/h2\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\u003cp\u003eThis section provides an overview of the page format used within \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e tables and indexes.\u003ca class=\"footnote\" href=\"/docs/18/storage-page-layout.html#ftn.id-1.10.18.8.2.2\"\u003e\u003csup class=\"footnote\"\u003e[19]\u003c/sup\u003e\u003c/a\u003e Sequences and TOAST tables are formatted just like a regular table.\u003c/p\u003e\n\u003cp\u003eIn the following explanation, a \u003cem class=\"firstterm\"\u003ebyte\u003c/em\u003e is assumed to contain 8 bits. In addition, the term \u003cem class=\"firstterm\"\u003eitem\u003c/em\u003e refers to an individual data value that is stored on a page. In a table, an item is a row; in an index, an item is an index entry.\u003c/p\u003e\n\u003cp\u003eEvery table and index is stored as an array of \u003cem class=\"firstterm\"\u003epages\u003c/em\u003e of a fixed size (usually 8 kB, although a different page size can be selected when compiling the server). In a table, all the pages are logically equivalent, so a particular item (row) can be stored in any page. In indexes, the first page is generally reserved as a \u003cem class=\"firstterm\"\u003emetapage\u003c/em\u003e holding control information, and there can be different types of pages within the index, depending on the index access method.\u003c/p\u003e\n\u003cp\u003e\u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#PAGE-TABLE\" title=\"Table 66.2. Overall Page Layout\"\u003eTable 66.2\u003c/a\u003e shows the overall layout of a page. There are five parts to each page.\u003c/p\u003e\n\u003cdiv class=\"table\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eTable 66.2. Overall Page Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"table-contents\"\u003e\n\u003ctable class=\"table\"\u003e\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eItem\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003ePageHeaderData\u003c/td\u003e\n\u003ctd\u003e24 bytes long. Contains general information about the page, including free space pointers.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eItemIdData\u003c/td\u003e\n\u003ctd\u003eArray of item identifiers pointing to the actual items. Each entry is an (offset,length) pair. 4 bytes per item.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eFree space\u003c/td\u003e\n\u003ctd\u003eThe unallocated space. New item identifiers are allocated from the start of this area, new items from the end.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eItems\u003c/td\u003e\n\u003ctd\u003eThe actual items themselves.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSpecial space\u003c/td\u003e\n\u003ctd\u003eIndex access method specific data. Different methods store different data. Empty in ordinary tables.\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"table-break\"\u003e\n\u003cp\u003eThe first 24 bytes of each page consists of a page header (\u003ccode class=\"structname\"\u003ePageHeaderData\u003c/code\u003e). Its format is detailed in \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#PAGEHEADERDATA-TABLE\" title=\"Table 66.3. PageHeaderData Layout\"\u003eTable 66.3\u003c/a\u003e. The first field tracks the most recent WAL entry related to this page. The second field contains the page checksum if \u003ca class=\"link\" href=\"/docs/18/checksums.html\" title=\"28.2. Data Checksums\"\u003edata checksums\u003c/a\u003e are enabled. Next is a 2-byte field containing flag bits. This is followed by three 2-byte integer fields (\u003ccode class=\"structfield\"\u003epd_lower\u003c/code\u003e, \u003ccode class=\"structfield\"\u003epd_upper\u003c/code\u003e, and \u003ccode class=\"structfield\"\u003epd_special\u003c/code\u003e). These contain byte offsets from the page start to the start of unallocated space, to the end of unallocated space, and to the start of the special space. The next 2 bytes of the page header, \u003ccode class=\"structfield\"\u003epd_pagesize_version\u003c/code\u003e, store both the page size and a version indicator. Beginning with \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 8.3 the version number is 4; \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 8.1 and 8.2 used version number 3; \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 8.0 used version number 2; \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 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 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. The last field is a hint that shows whether pruning the page is likely to be profitable: it tracks the oldest un-pruned XMAX on the page.\u003c/p\u003e\n\u003cdiv class=\"table\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eTable 66.3. PageHeaderData Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"table-contents\"\u003e\n\u003ctable class=\"table\"\u003e\n\n\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eLength\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_lsn\u003c/td\u003e\n\u003ctd\u003ePageXLogRecPtr\u003c/td\u003e\n\u003ctd\u003e8 bytes\u003c/td\u003e\n\u003ctd\u003eLSN: next byte after last byte of WAL record for last change to this page\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_checksum\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003ePage checksum\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_flags\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eFlag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_lower\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to start of free space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_upper\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to end of free space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_special\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to start of special space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_pagesize_version\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003ePage size and layout version number information\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_prune_xid\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003eOldest unpruned XMAX on page, or zero if none\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"table-break\"\u003e\n\u003cp\u003eAll the details can be found in \u003ccode class=\"filename\"\u003esrc/include/storage/bufpage.h\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFollowing the page header are item identifiers (\u003ccode class=\"type\"\u003eItemIdData\u003c/code\u003e), each requiring four bytes. An item identifier contains a byte-offset to the start of an item, its length in bytes, and a few attribute bits which affect its interpretation. New item identifiers are allocated as needed from the beginning of the unallocated space. The number of item identifiers present can be determined by looking at \u003ccode class=\"structfield\"\u003epd_lower\u003c/code\u003e, which is increased to allocate a new identifier. Because an item identifier is never moved until it is freed, its index can be used on a long-term basis to reference an item, even when the item itself is moved around on the page to compact free space. In fact, every pointer to an item (\u003ccode class=\"type\"\u003eItemPointer\u003c/code\u003e, also known as \u003ccode class=\"type\"\u003eCTID\u003c/code\u003e) created by \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e consists of a page number and the index of an item identifier.\u003c/p\u003e\n\u003cp\u003eThe items themselves are stored in space allocated backwards from the end of unallocated space. The exact structure varies depending on what the table is to contain. Tables and sequences both use a structure named \u003ccode class=\"type\"\u003eHeapTupleHeaderData\u003c/code\u003e, described below.\u003c/p\u003e\n\u003cp\u003eThe final section is the \u003cspan class=\"quote\"\u003e“\u003cspan class=\"quote\"\u003especial section\u003c/span\u003e”\u003c/span\u003e which can contain anything the access method wishes to store. For example, b-tree indexes store links to the page's left and right siblings, as well as some other data relevant to the index structure. Ordinary tables do not use a special section at all (indicated by setting \u003ccode class=\"structfield\"\u003epd_special\u003c/code\u003e to equal the page size).\u003c/p\u003e\n\u003cp\u003e\u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#STORAGE-PAGE-LAYOUT-FIGURE\" title=\"Figure 66.1. Page Layout\"\u003eFigure 66.1\u003c/a\u003e illustrates how these parts are laid out in a page.\u003c/p\u003e\n\u003cdiv class=\"figure col-xl-8 col-lg-10 col-md-12\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eFigure 66.1. Page Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"figure-contents\"\u003e\n\u003cdiv class=\"mediaobject\"\u003e\n\n\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"figure-break\"\u003e\n\u003cdiv class=\"sect2\"\u003e\n\u003cdiv class=\"titlepage\"\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch3 class=\"title\"\u003e66.6.1. Table Row Layout \u003c/h3\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003eAll table rows are structured in the same way. There is a fixed-size header (occupying 23 bytes on most machines), followed by an optional null bitmap, an optional object ID field, and the user data. The header is detailed in \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#HEAPTUPLEHEADERDATA-TABLE\" title=\"Table 66.4. HeapTupleHeaderData Layout\"\u003eTable 66.4\u003c/a\u003e. The actual user data (columns of the row) begins at the offset indicated by \u003ccode class=\"structfield\"\u003et_hoff\u003c/code\u003e, which must always be a multiple of the MAXALIGN distance for the platform. The null bitmap is only present if the \u003cem class=\"firstterm\"\u003eHEAP_HASNULL\u003c/em\u003e bit is set in \u003ccode class=\"structfield\"\u003et_infomask\u003c/code\u003e. If it is present it begins just after the fixed header and occupies enough bytes to have one bit per data column (that is, the number of bits that equals the attribute count in \u003ccode class=\"structfield\"\u003et_infomask2\u003c/code\u003e). In this list of bits, a 1 bit indicates not-null, a 0 bit is a null. When the bitmap is not present, all columns are assumed not-null. The object ID is only present if the \u003cem class=\"firstterm\"\u003eHEAP_HASOID_OLD\u003c/em\u003e bit is set in \u003ccode class=\"structfield\"\u003et_infomask\u003c/code\u003e. If present, it appears just before the \u003ccode class=\"structfield\"\u003et_hoff\u003c/code\u003e boundary. Any padding needed to make \u003ccode class=\"structfield\"\u003et_hoff\u003c/code\u003e a MAXALIGN multiple will appear between the null bitmap and the object ID. (This in turn ensures that the object ID is suitably aligned.)\u003c/p\u003e\n\u003cdiv class=\"table\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eTable 66.4. HeapTupleHeaderData Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"table-contents\"\u003e\n\u003ctable class=\"table\"\u003e\n\n\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eLength\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xmin\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003einsert XID stamp\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xmax\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003edelete XID stamp\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_cid\u003c/td\u003e\n\u003ctd\u003eCommandId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003einsert and/or delete CID stamp (overlays with t_xvac)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xvac\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003eXID for VACUUM operation moving a row version\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_ctid\u003c/td\u003e\n\u003ctd\u003eItemPointerData\u003c/td\u003e\n\u003ctd\u003e6 bytes\u003c/td\u003e\n\u003ctd\u003ecurrent TID of this or newer row version\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_infomask2\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003enumber of attributes, plus various flag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_infomask\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003evarious flag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_hoff\u003c/td\u003e\n\u003ctd\u003euint8\u003c/td\u003e\n\u003ctd\u003e1 byte\u003c/td\u003e\n\u003ctd\u003eoffset to user data\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"table-break\"\u003e\n\u003cp\u003eAll the details can be found in \u003ccode class=\"filename\"\u003esrc/include/access/htup_details.h\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eInterpreting the actual data can only be done with information obtained from other tables, mostly \u003ccode class=\"structname\"\u003epg_attribute\u003c/code\u003e. The key values needed to identify field locations are \u003ccode class=\"structfield\"\u003eattlen\u003c/code\u003e and \u003ccode class=\"structfield\"\u003eattalign\u003c/code\u003e. There is no way to directly get a particular attribute, except when there are only fixed width fields and no null values. All this trickery is wrapped up in the functions \u003cem class=\"firstterm\"\u003eheap_getattr\u003c/em\u003e, \u003cem class=\"firstterm\"\u003efastgetattr\u003c/em\u003e and \u003cem class=\"firstterm\"\u003eheap_getsysattr\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eTo read the data you need to examine each attribute in turn. First check whether the field is NULL according to the null bitmap. If it is, go to the next. Then make sure you have the right alignment. If the field is a fixed width field, then all the bytes are simply placed. If it's a variable length field (attlen = -1) then it's a bit more complicated. All variable-length data types share the common header structure \u003ccode class=\"type\"\u003estruct varlena\u003c/code\u003e, which includes the total length of the stored value and some flag bits. Depending on the flags, the data can be either inline or in a TOAST table; it might be compressed, too (see \u003ca class=\"xref\" href=\"/docs/18/storage-toast.html\" title=\"66.2. TOAST\"\u003eSection 66.2\u003c/a\u003e).\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv class=\"footnotes\"\u003e\n\u003cbr\u003e\n\u003chr\u003e\n\u003cdiv class=\"footnote\"\u003e\n\u003cp\u003e\u003ca class=\"para\" href=\"/docs/18/storage-page-layout.html#id-1.10.18.8.2.2\"\u003e\u003csup class=\"para\"\u003e[19]\u003c/sup\u003e\u003c/a\u003e Actually, use of this page format is not required for either table or index access methods. The \u003ccode class=\"literal\"\u003eheap\u003c/code\u003e table access method always uses this format. All the existing index methods also use the basic format, but the data kept on index metapages usually doesn't follow the item layout rules.\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e","manual_path":"/docs/18/storage-page-layout.html","related":[{"label":"Table AM","url":"/wiki/tableam/?v=18"},{"label":"Storage Parameters","url":"/wiki/relopts/?v=18"}],"release":{"channel":"stable","label":"18.6","major":"18","ref":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2","revision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f"},"sections":[],"signature":"","sources":[{"label":"PostgreSQL 18 English manual","path":"storage-page-layout.html","sha256":"0d9f99e4cef5ea0a6741a74cab87052b929f7001730853c85d5e60d72179d355","url":"/docs/18/storage-page-layout.html"}],"tables":[{"columns":[{"key":"c0","label":"Item"},{"key":"c1","label":"Description"}],"key":"layout-0","rows":[{"c0":"PageHeaderData","c1":"24 bytes long. Contains general information about the page, including free space pointers."},{"c0":"ItemIdData","c1":"Array of item identifiers pointing to the actual items. Each entry is an (offset,length) pair. 4 bytes per item."},{"c0":"Free space","c1":"The unallocated space. New item identifiers are allocated from the start of this area, new items from the end."},{"c0":"Items","c1":"The actual items themselves."},{"c0":"Special space","c1":"Index access method specific data. Different methods store different data. Empty in ordinary tables."}],"title":"Overall Page Layout"},{"columns":[{"key":"c0","label":"Field"},{"key":"c1","label":"Type"},{"key":"c2","label":"Length"},{"key":"c3","label":"Description"}],"key":"layout-1","rows":[{"c0":"pd_lsn","c1":"PageXLogRecPtr","c2":"8 bytes","c3":"LSN: next byte after last byte of WAL record for last change to this page"},{"c0":"pd_checksum","c1":"uint16","c2":"2 bytes","c3":"Page checksum"},{"c0":"pd_flags","c1":"uint16","c2":"2 bytes","c3":"Flag bits"},{"c0":"pd_lower","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to start of free space"},{"c0":"pd_upper","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to end of free space"},{"c0":"pd_special","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to start of special space"},{"c0":"pd_pagesize_version","c1":"uint16","c2":"2 bytes","c3":"Page size and layout version number information"},{"c0":"pd_prune_xid","c1":"TransactionId","c2":"4 bytes","c3":"Oldest unpruned XMAX on page, or zero if none"}],"title":"PageHeaderData Layout"},{"columns":[{"key":"c0","label":"Field"},{"key":"c1","label":"Type"},{"key":"c2","label":"Length"},{"key":"c3","label":"Description"}],"key":"layout-2","rows":[{"c0":"t_xmin","c1":"TransactionId","c2":"4 bytes","c3":"insert XID stamp"},{"c0":"t_xmax","c1":"TransactionId","c2":"4 bytes","c3":"delete XID stamp"},{"c0":"t_cid","c1":"CommandId","c2":"4 bytes","c3":"insert and/or delete CID stamp (overlays with t_xvac)"},{"c0":"t_xvac","c1":"TransactionId","c2":"4 bytes","c3":"XID for VACUUM operation moving a row version"},{"c0":"t_ctid","c1":"ItemPointerData","c2":"6 bytes","c3":"current TID of this or newer row version"},{"c0":"t_infomask2","c1":"uint16","c2":"2 bytes","c3":"number of attributes, plus various flag bits"},{"c0":"t_infomask","c1":"uint16","c2":"2 bytes","c3":"various flag bits"},{"c0":"t_hoff","c1":"uint8","c2":"1 byte","c3":"offset to user data"}],"title":"HeapTupleHeaderData Layout"}]},"ManualEvidence":{"manual_path":"/docs/18/storage-page-layout.html","release":{"channel":"stable","label":"18.6","major":"18","ref":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2","revision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f"},"sources":[{"label":"PostgreSQL 18 English manual","path":"storage-page-layout.html","sha256":"0d9f99e4cef5ea0a6741a74cab87052b929f7001730853c85d5e60d72179d355","url":"/docs/18/storage-page-layout.html"}]},"MeasuredEvidence":{}},"Text":{"Collection":"storage","Key":"storage-page-layout","SourceDatabase":"center","Version":"18","Locale":"en","Title":"Database Page Layout","Summary":"This section provides an overview of the page format used within PostgreSQL tables and indexes. [19] Sequences and TOAST tables are formatted just like a regular table.","BodyHTML":"\u003cdiv\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch2\u003e66.6. Database Page Layout \u003c/h2\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\u003cp\u003eThis section provides an overview of the page format used within \u003cspan\u003ePostgreSQL\u003c/span\u003e tables and indexes.\u003ca href=\"/docs/18/storage-page-layout.html#ftn.id-1.10.18.8.2.2\" rel=\"nofollow\"\u003e\u003csup\u003e[19]\u003c/sup\u003e\u003c/a\u003e Sequences and TOAST tables are formatted just like a regular table.\u003c/p\u003e\n\u003cp\u003eIn the following explanation, a \u003cem\u003ebyte\u003c/em\u003e is assumed to contain 8 bits. In addition, the term \u003cem\u003eitem\u003c/em\u003e refers to an individual data value that is stored on a page. In a table, an item is a row; in an index, an item is an index entry.\u003c/p\u003e\n\u003cp\u003eEvery table and index is stored as an array of \u003cem\u003epages\u003c/em\u003e of a fixed size (usually 8 kB, although a different page size can be selected when compiling the server). In a table, all the pages are logically equivalent, so a particular item (row) can be stored in any page. In indexes, the first page is generally reserved as a \u003cem\u003emetapage\u003c/em\u003e holding control information, and there can be different types of pages within the index, depending on the index access method.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/docs/18/storage-page-layout.html#PAGE-TABLE\" rel=\"nofollow\"\u003eTable 66.2\u003c/a\u003e shows the overall layout of a page. There are five parts to each page.\u003c/p\u003e\n\u003cdiv\u003e\n\u003cp\u003e\u003cstrong\u003eTable 66.2. Overall Page Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv\u003e\n\u003ctable\u003e\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eItem\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003ePageHeaderData\u003c/td\u003e\n\u003ctd\u003e24 bytes long. Contains general information about the page, including free space pointers.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eItemIdData\u003c/td\u003e\n\u003ctd\u003eArray of item identifiers pointing to the actual items. Each entry is an (offset,length) pair. 4 bytes per item.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eFree space\u003c/td\u003e\n\u003ctd\u003eThe unallocated space. New item identifiers are allocated from the start of this area, new items from the end.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eItems\u003c/td\u003e\n\u003ctd\u003eThe actual items themselves.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSpecial space\u003c/td\u003e\n\u003ctd\u003eIndex access method specific data. Different methods store different data. Empty in ordinary tables.\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr\u003e\n\u003cp\u003eThe first 24 bytes of each page consists of a page header (\u003ccode\u003ePageHeaderData\u003c/code\u003e). Its format is detailed in \u003ca href=\"/docs/18/storage-page-layout.html#PAGEHEADERDATA-TABLE\" rel=\"nofollow\"\u003eTable 66.3\u003c/a\u003e. The first field tracks the most recent WAL entry related to this page. The second field contains the page checksum if \u003ca href=\"/docs/18/checksums.html\" rel=\"nofollow\"\u003edata checksums\u003c/a\u003e are enabled. Next is a 2-byte field containing flag bits. This is followed by three 2-byte integer fields (\u003ccode\u003epd_lower\u003c/code\u003e, \u003ccode\u003epd_upper\u003c/code\u003e, and \u003ccode\u003epd_special\u003c/code\u003e). These contain byte offsets from the page start to the start of unallocated space, to the end of unallocated space, and to the start of the special space. The next 2 bytes of the page header, \u003ccode\u003epd_pagesize_version\u003c/code\u003e, store both the page size and a version indicator. Beginning with \u003cspan\u003ePostgreSQL\u003c/span\u003e 8.3 the version number is 4; \u003cspan\u003ePostgreSQL\u003c/span\u003e 8.1 and 8.2 used version number 3; \u003cspan\u003ePostgreSQL\u003c/span\u003e 8.0 used version number 2; \u003cspan\u003ePostgreSQL\u003c/span\u003e 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 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. The last field is a hint that shows whether pruning the page is likely to be profitable: it tracks the oldest un-pruned XMAX on the page.\u003c/p\u003e\n\u003cdiv\u003e\n\u003cp\u003e\u003cstrong\u003eTable 66.3. PageHeaderData Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv\u003e\n\u003ctable\u003e\n\n\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eLength\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_lsn\u003c/td\u003e\n\u003ctd\u003ePageXLogRecPtr\u003c/td\u003e\n\u003ctd\u003e8 bytes\u003c/td\u003e\n\u003ctd\u003eLSN: next byte after last byte of WAL record for last change to this page\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_checksum\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003ePage checksum\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_flags\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eFlag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_lower\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to start of free space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_upper\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to end of free space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_special\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to start of special space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_pagesize_version\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003ePage size and layout version number information\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_prune_xid\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003eOldest unpruned XMAX on page, or zero if none\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr\u003e\n\u003cp\u003eAll the details can be found in \u003ccode\u003esrc/include/storage/bufpage.h\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFollowing the page header are item identifiers (\u003ccode\u003eItemIdData\u003c/code\u003e), each requiring four bytes. An item identifier contains a byte-offset to the start of an item, its length in bytes, and a few attribute bits which affect its interpretation. New item identifiers are allocated as needed from the beginning of the unallocated space. The number of item identifiers present can be determined by looking at \u003ccode\u003epd_lower\u003c/code\u003e, which is increased to allocate a new identifier. Because an item identifier is never moved until it is freed, its index can be used on a long-term basis to reference an item, even when the item itself is moved around on the page to compact free space. In fact, every pointer to an item (\u003ccode\u003eItemPointer\u003c/code\u003e, also known as \u003ccode\u003eCTID\u003c/code\u003e) created by \u003cspan\u003ePostgreSQL\u003c/span\u003e consists of a page number and the index of an item identifier.\u003c/p\u003e\n\u003cp\u003eThe items themselves are stored in space allocated backwards from the end of unallocated space. The exact structure varies depending on what the table is to contain. Tables and sequences both use a structure named \u003ccode\u003eHeapTupleHeaderData\u003c/code\u003e, described below.\u003c/p\u003e\n\u003cp\u003eThe final section is the \u003cspan\u003e“\u003cspan\u003especial section\u003c/span\u003e”\u003c/span\u003e which can contain anything the access method wishes to store. For example, b-tree indexes store links to the page\u0026#39;s left and right siblings, as well as some other data relevant to the index structure. Ordinary tables do not use a special section at all (indicated by setting \u003ccode\u003epd_special\u003c/code\u003e to equal the page size).\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/docs/18/storage-page-layout.html#STORAGE-PAGE-LAYOUT-FIGURE\" rel=\"nofollow\"\u003eFigure 66.1\u003c/a\u003e illustrates how these parts are laid out in a page.\u003c/p\u003e\n\u003cdiv\u003e\n\u003cp\u003e\u003cstrong\u003eFigure 66.1. Page Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\n\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch3\u003e66.6.1. Table Row Layout \u003c/h3\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003eAll table rows are structured in the same way. There is a fixed-size header (occupying 23 bytes on most machines), followed by an optional null bitmap, an optional object ID field, and the user data. The header is detailed in \u003ca href=\"/docs/18/storage-page-layout.html#HEAPTUPLEHEADERDATA-TABLE\" rel=\"nofollow\"\u003eTable 66.4\u003c/a\u003e. The actual user data (columns of the row) begins at the offset indicated by \u003ccode\u003et_hoff\u003c/code\u003e, which must always be a multiple of the MAXALIGN distance for the platform. The null bitmap is only present if the \u003cem\u003eHEAP_HASNULL\u003c/em\u003e bit is set in \u003ccode\u003et_infomask\u003c/code\u003e. If it is present it begins just after the fixed header and occupies enough bytes to have one bit per data column (that is, the number of bits that equals the attribute count in \u003ccode\u003et_infomask2\u003c/code\u003e). In this list of bits, a 1 bit indicates not-null, a 0 bit is a null. When the bitmap is not present, all columns are assumed not-null. The object ID is only present if the \u003cem\u003eHEAP_HASOID_OLD\u003c/em\u003e bit is set in \u003ccode\u003et_infomask\u003c/code\u003e. If present, it appears just before the \u003ccode\u003et_hoff\u003c/code\u003e boundary. Any padding needed to make \u003ccode\u003et_hoff\u003c/code\u003e a MAXALIGN multiple will appear between the null bitmap and the object ID. (This in turn ensures that the object ID is suitably aligned.)\u003c/p\u003e\n\u003cdiv\u003e\n\u003cp\u003e\u003cstrong\u003eTable 66.4. HeapTupleHeaderData Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv\u003e\n\u003ctable\u003e\n\n\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eLength\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xmin\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003einsert XID stamp\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xmax\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003edelete XID stamp\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_cid\u003c/td\u003e\n\u003ctd\u003eCommandId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003einsert and/or delete CID stamp (overlays with t_xvac)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xvac\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003eXID for VACUUM operation moving a row version\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_ctid\u003c/td\u003e\n\u003ctd\u003eItemPointerData\u003c/td\u003e\n\u003ctd\u003e6 bytes\u003c/td\u003e\n\u003ctd\u003ecurrent TID of this or newer row version\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_infomask2\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003enumber of attributes, plus various flag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_infomask\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003evarious flag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_hoff\u003c/td\u003e\n\u003ctd\u003euint8\u003c/td\u003e\n\u003ctd\u003e1 byte\u003c/td\u003e\n\u003ctd\u003eoffset to user data\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr\u003e\n\u003cp\u003eAll the details can be found in \u003ccode\u003esrc/include/access/htup_details.h\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eInterpreting the actual data can only be done with information obtained from other tables, mostly \u003ccode\u003epg_attribute\u003c/code\u003e. The key values needed to identify field locations are \u003ccode\u003eattlen\u003c/code\u003e and \u003ccode\u003eattalign\u003c/code\u003e. There is no way to directly get a particular attribute, except when there are only fixed width fields and no null values. All this trickery is wrapped up in the functions \u003cem\u003eheap_getattr\u003c/em\u003e, \u003cem\u003efastgetattr\u003c/em\u003e and \u003cem\u003eheap_getsysattr\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eTo read the data you need to examine each attribute in turn. First check whether the field is NULL according to the null bitmap. If it is, go to the next. Then make sure you have the right alignment. If the field is a fixed width field, then all the bytes are simply placed. If it\u0026#39;s a variable length field (attlen = -1) then it\u0026#39;s a bit more complicated. All variable-length data types share the common header structure \u003ccode\u003estruct varlena\u003c/code\u003e, which includes the total length of the stored value and some flag bits. Depending on the flags, the data can be either inline or in a TOAST table; it might be compressed, too (see \u003ca href=\"/docs/18/storage-toast.html\" rel=\"nofollow\"\u003eSection 66.2\u003c/a\u003e).\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003cbr\u003e\n\u003chr\u003e\n\u003cdiv\u003e\n\u003cp\u003e\u003ca href=\"/docs/18/storage-page-layout.html#id-1.10.18.8.2.2\" rel=\"nofollow\"\u003e\u003csup\u003e[19]\u003c/sup\u003e\u003c/a\u003e Actually, use of this page format is not required for either table or index access methods. The \u003ccode\u003eheap\u003c/code\u003e table access method always uses this format. All the existing index methods also use the basic format, but the data kept on index metapages usually doesn\u0026#39;t follow the item layout rules.\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","ContentHash":"328660f6e0db0fe51e09b0da5cca4d437ba6662938e8c02e8d5e628b8573e75f","Payload":{"description":["This section provides an overview of the page format used within PostgreSQL tables and indexes. [19] Sequences and TOAST tables are formatted just like a regular table."],"manual_html":"\u003cdiv class=\"sect1\"\u003e\n\u003cdiv class=\"titlepage\"\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch2 class=\"title\"\u003e66.6. Database Page Layout \u003c/h2\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\u003cp\u003eThis section provides an overview of the page format used within \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e tables and indexes.\u003ca class=\"footnote\" href=\"/docs/18/storage-page-layout.html#ftn.id-1.10.18.8.2.2\"\u003e\u003csup class=\"footnote\"\u003e[19]\u003c/sup\u003e\u003c/a\u003e Sequences and TOAST tables are formatted just like a regular table.\u003c/p\u003e\n\u003cp\u003eIn the following explanation, a \u003cem class=\"firstterm\"\u003ebyte\u003c/em\u003e is assumed to contain 8 bits. In addition, the term \u003cem class=\"firstterm\"\u003eitem\u003c/em\u003e refers to an individual data value that is stored on a page. In a table, an item is a row; in an index, an item is an index entry.\u003c/p\u003e\n\u003cp\u003eEvery table and index is stored as an array of \u003cem class=\"firstterm\"\u003epages\u003c/em\u003e of a fixed size (usually 8 kB, although a different page size can be selected when compiling the server). In a table, all the pages are logically equivalent, so a particular item (row) can be stored in any page. In indexes, the first page is generally reserved as a \u003cem class=\"firstterm\"\u003emetapage\u003c/em\u003e holding control information, and there can be different types of pages within the index, depending on the index access method.\u003c/p\u003e\n\u003cp\u003e\u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#PAGE-TABLE\" title=\"Table 66.2. Overall Page Layout\"\u003eTable 66.2\u003c/a\u003e shows the overall layout of a page. There are five parts to each page.\u003c/p\u003e\n\u003cdiv class=\"table\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eTable 66.2. Overall Page Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"table-contents\"\u003e\n\u003ctable class=\"table\"\u003e\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eItem\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003ePageHeaderData\u003c/td\u003e\n\u003ctd\u003e24 bytes long. Contains general information about the page, including free space pointers.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eItemIdData\u003c/td\u003e\n\u003ctd\u003eArray of item identifiers pointing to the actual items. Each entry is an (offset,length) pair. 4 bytes per item.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eFree space\u003c/td\u003e\n\u003ctd\u003eThe unallocated space. New item identifiers are allocated from the start of this area, new items from the end.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eItems\u003c/td\u003e\n\u003ctd\u003eThe actual items themselves.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSpecial space\u003c/td\u003e\n\u003ctd\u003eIndex access method specific data. Different methods store different data. Empty in ordinary tables.\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"table-break\"\u003e\n\u003cp\u003eThe first 24 bytes of each page consists of a page header (\u003ccode class=\"structname\"\u003ePageHeaderData\u003c/code\u003e). Its format is detailed in \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#PAGEHEADERDATA-TABLE\" title=\"Table 66.3. PageHeaderData Layout\"\u003eTable 66.3\u003c/a\u003e. The first field tracks the most recent WAL entry related to this page. The second field contains the page checksum if \u003ca class=\"link\" href=\"/docs/18/checksums.html\" title=\"28.2. Data Checksums\"\u003edata checksums\u003c/a\u003e are enabled. Next is a 2-byte field containing flag bits. This is followed by three 2-byte integer fields (\u003ccode class=\"structfield\"\u003epd_lower\u003c/code\u003e, \u003ccode class=\"structfield\"\u003epd_upper\u003c/code\u003e, and \u003ccode class=\"structfield\"\u003epd_special\u003c/code\u003e). These contain byte offsets from the page start to the start of unallocated space, to the end of unallocated space, and to the start of the special space. The next 2 bytes of the page header, \u003ccode class=\"structfield\"\u003epd_pagesize_version\u003c/code\u003e, store both the page size and a version indicator. Beginning with \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 8.3 the version number is 4; \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 8.1 and 8.2 used version number 3; \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 8.0 used version number 2; \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 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 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. The last field is a hint that shows whether pruning the page is likely to be profitable: it tracks the oldest un-pruned XMAX on the page.\u003c/p\u003e\n\u003cdiv class=\"table\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eTable 66.3. PageHeaderData Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"table-contents\"\u003e\n\u003ctable class=\"table\"\u003e\n\n\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eLength\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_lsn\u003c/td\u003e\n\u003ctd\u003ePageXLogRecPtr\u003c/td\u003e\n\u003ctd\u003e8 bytes\u003c/td\u003e\n\u003ctd\u003eLSN: next byte after last byte of WAL record for last change to this page\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_checksum\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003ePage checksum\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_flags\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eFlag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_lower\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to start of free space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_upper\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to end of free space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_special\u003c/td\u003e\n\u003ctd\u003eLocationIndex\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003eOffset to start of special space\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_pagesize_version\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003ePage size and layout version number information\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003epd_prune_xid\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003eOldest unpruned XMAX on page, or zero if none\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"table-break\"\u003e\n\u003cp\u003eAll the details can be found in \u003ccode class=\"filename\"\u003esrc/include/storage/bufpage.h\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFollowing the page header are item identifiers (\u003ccode class=\"type\"\u003eItemIdData\u003c/code\u003e), each requiring four bytes. An item identifier contains a byte-offset to the start of an item, its length in bytes, and a few attribute bits which affect its interpretation. New item identifiers are allocated as needed from the beginning of the unallocated space. The number of item identifiers present can be determined by looking at \u003ccode class=\"structfield\"\u003epd_lower\u003c/code\u003e, which is increased to allocate a new identifier. Because an item identifier is never moved until it is freed, its index can be used on a long-term basis to reference an item, even when the item itself is moved around on the page to compact free space. In fact, every pointer to an item (\u003ccode class=\"type\"\u003eItemPointer\u003c/code\u003e, also known as \u003ccode class=\"type\"\u003eCTID\u003c/code\u003e) created by \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e consists of a page number and the index of an item identifier.\u003c/p\u003e\n\u003cp\u003eThe items themselves are stored in space allocated backwards from the end of unallocated space. The exact structure varies depending on what the table is to contain. Tables and sequences both use a structure named \u003ccode class=\"type\"\u003eHeapTupleHeaderData\u003c/code\u003e, described below.\u003c/p\u003e\n\u003cp\u003eThe final section is the \u003cspan class=\"quote\"\u003e“\u003cspan class=\"quote\"\u003especial section\u003c/span\u003e”\u003c/span\u003e which can contain anything the access method wishes to store. For example, b-tree indexes store links to the page's left and right siblings, as well as some other data relevant to the index structure. Ordinary tables do not use a special section at all (indicated by setting \u003ccode class=\"structfield\"\u003epd_special\u003c/code\u003e to equal the page size).\u003c/p\u003e\n\u003cp\u003e\u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#STORAGE-PAGE-LAYOUT-FIGURE\" title=\"Figure 66.1. Page Layout\"\u003eFigure 66.1\u003c/a\u003e illustrates how these parts are laid out in a page.\u003c/p\u003e\n\u003cdiv class=\"figure col-xl-8 col-lg-10 col-md-12\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eFigure 66.1. Page Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"figure-contents\"\u003e\n\u003cdiv class=\"mediaobject\"\u003e\n\n\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"figure-break\"\u003e\n\u003cdiv class=\"sect2\"\u003e\n\u003cdiv class=\"titlepage\"\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch3 class=\"title\"\u003e66.6.1. Table Row Layout \u003c/h3\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003eAll table rows are structured in the same way. There is a fixed-size header (occupying 23 bytes on most machines), followed by an optional null bitmap, an optional object ID field, and the user data. The header is detailed in \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html#HEAPTUPLEHEADERDATA-TABLE\" title=\"Table 66.4. HeapTupleHeaderData Layout\"\u003eTable 66.4\u003c/a\u003e. The actual user data (columns of the row) begins at the offset indicated by \u003ccode class=\"structfield\"\u003et_hoff\u003c/code\u003e, which must always be a multiple of the MAXALIGN distance for the platform. The null bitmap is only present if the \u003cem class=\"firstterm\"\u003eHEAP_HASNULL\u003c/em\u003e bit is set in \u003ccode class=\"structfield\"\u003et_infomask\u003c/code\u003e. If it is present it begins just after the fixed header and occupies enough bytes to have one bit per data column (that is, the number of bits that equals the attribute count in \u003ccode class=\"structfield\"\u003et_infomask2\u003c/code\u003e). In this list of bits, a 1 bit indicates not-null, a 0 bit is a null. When the bitmap is not present, all columns are assumed not-null. The object ID is only present if the \u003cem class=\"firstterm\"\u003eHEAP_HASOID_OLD\u003c/em\u003e bit is set in \u003ccode class=\"structfield\"\u003et_infomask\u003c/code\u003e. If present, it appears just before the \u003ccode class=\"structfield\"\u003et_hoff\u003c/code\u003e boundary. Any padding needed to make \u003ccode class=\"structfield\"\u003et_hoff\u003c/code\u003e a MAXALIGN multiple will appear between the null bitmap and the object ID. (This in turn ensures that the object ID is suitably aligned.)\u003c/p\u003e\n\u003cdiv class=\"table\"\u003e\n\u003cp class=\"title\"\u003e\u003cstrong\u003eTable 66.4. HeapTupleHeaderData Layout\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"table-contents\"\u003e\n\u003ctable class=\"table\"\u003e\n\n\n\n\n\n\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eLength\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xmin\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003einsert XID stamp\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xmax\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003edelete XID stamp\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_cid\u003c/td\u003e\n\u003ctd\u003eCommandId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003einsert and/or delete CID stamp (overlays with t_xvac)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_xvac\u003c/td\u003e\n\u003ctd\u003eTransactionId\u003c/td\u003e\n\u003ctd\u003e4 bytes\u003c/td\u003e\n\u003ctd\u003eXID for VACUUM operation moving a row version\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_ctid\u003c/td\u003e\n\u003ctd\u003eItemPointerData\u003c/td\u003e\n\u003ctd\u003e6 bytes\u003c/td\u003e\n\u003ctd\u003ecurrent TID of this or newer row version\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_infomask2\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003enumber of attributes, plus various flag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_infomask\u003c/td\u003e\n\u003ctd\u003euint16\u003c/td\u003e\n\u003ctd\u003e2 bytes\u003c/td\u003e\n\u003ctd\u003evarious flag bits\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003et_hoff\u003c/td\u003e\n\u003ctd\u003euint8\u003c/td\u003e\n\u003ctd\u003e1 byte\u003c/td\u003e\n\u003ctd\u003eoffset to user data\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cbr class=\"table-break\"\u003e\n\u003cp\u003eAll the details can be found in \u003ccode class=\"filename\"\u003esrc/include/access/htup_details.h\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eInterpreting the actual data can only be done with information obtained from other tables, mostly \u003ccode class=\"structname\"\u003epg_attribute\u003c/code\u003e. The key values needed to identify field locations are \u003ccode class=\"structfield\"\u003eattlen\u003c/code\u003e and \u003ccode class=\"structfield\"\u003eattalign\u003c/code\u003e. There is no way to directly get a particular attribute, except when there are only fixed width fields and no null values. All this trickery is wrapped up in the functions \u003cem class=\"firstterm\"\u003eheap_getattr\u003c/em\u003e, \u003cem class=\"firstterm\"\u003efastgetattr\u003c/em\u003e and \u003cem class=\"firstterm\"\u003eheap_getsysattr\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eTo read the data you need to examine each attribute in turn. First check whether the field is NULL according to the null bitmap. If it is, go to the next. Then make sure you have the right alignment. If the field is a fixed width field, then all the bytes are simply placed. If it's a variable length field (attlen = -1) then it's a bit more complicated. All variable-length data types share the common header structure \u003ccode class=\"type\"\u003estruct varlena\u003c/code\u003e, which includes the total length of the stored value and some flag bits. Depending on the flags, the data can be either inline or in a TOAST table; it might be compressed, too (see \u003ca class=\"xref\" href=\"/docs/18/storage-toast.html\" title=\"66.2. TOAST\"\u003eSection 66.2\u003c/a\u003e).\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv class=\"footnotes\"\u003e\n\u003cbr\u003e\n\u003chr\u003e\n\u003cdiv class=\"footnote\"\u003e\n\u003cp\u003e\u003ca class=\"para\" href=\"/docs/18/storage-page-layout.html#id-1.10.18.8.2.2\"\u003e\u003csup class=\"para\"\u003e[19]\u003c/sup\u003e\u003c/a\u003e Actually, use of this page format is not required for either table or index access methods. The \u003ccode class=\"literal\"\u003eheap\u003c/code\u003e table access method always uses this format. All the existing index methods also use the basic format, but the data kept on index metapages usually doesn't follow the item layout rules.\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e","related":[{"label":"Table AM","url":"/wiki/tableam/?v=18"},{"label":"Storage Parameters","url":"/wiki/relopts/?v=18"}],"sections":[],"tables":[{"columns":[{"key":"c0","label":"Item"},{"key":"c1","label":"Description"}],"key":"layout-0","rows":[{"c0":"PageHeaderData","c1":"24 bytes long. Contains general information about the page, including free space pointers."},{"c0":"ItemIdData","c1":"Array of item identifiers pointing to the actual items. Each entry is an (offset,length) pair. 4 bytes per item."},{"c0":"Free space","c1":"The unallocated space. New item identifiers are allocated from the start of this area, new items from the end."},{"c0":"Items","c1":"The actual items themselves."},{"c0":"Special space","c1":"Index access method specific data. Different methods store different data. Empty in ordinary tables."}],"title":"Overall Page Layout"},{"columns":[{"key":"c0","label":"Field"},{"key":"c1","label":"Type"},{"key":"c2","label":"Length"},{"key":"c3","label":"Description"}],"key":"layout-1","rows":[{"c0":"pd_lsn","c1":"PageXLogRecPtr","c2":"8 bytes","c3":"LSN: next byte after last byte of WAL record for last change to this page"},{"c0":"pd_checksum","c1":"uint16","c2":"2 bytes","c3":"Page checksum"},{"c0":"pd_flags","c1":"uint16","c2":"2 bytes","c3":"Flag bits"},{"c0":"pd_lower","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to start of free space"},{"c0":"pd_upper","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to end of free space"},{"c0":"pd_special","c1":"LocationIndex","c2":"2 bytes","c3":"Offset to start of special space"},{"c0":"pd_pagesize_version","c1":"uint16","c2":"2 bytes","c3":"Page size and layout version number information"},{"c0":"pd_prune_xid","c1":"TransactionId","c2":"4 bytes","c3":"Oldest unpruned XMAX on page, or zero if none"}],"title":"PageHeaderData Layout"},{"columns":[{"key":"c0","label":"Field"},{"key":"c1","label":"Type"},{"key":"c2","label":"Length"},{"key":"c3","label":"Description"}],"key":"layout-2","rows":[{"c0":"t_xmin","c1":"TransactionId","c2":"4 bytes","c3":"insert XID stamp"},{"c0":"t_xmax","c1":"TransactionId","c2":"4 bytes","c3":"delete XID stamp"},{"c0":"t_cid","c1":"CommandId","c2":"4 bytes","c3":"insert and/or delete CID stamp (overlays with t_xvac)"},{"c0":"t_xvac","c1":"TransactionId","c2":"4 bytes","c3":"XID for VACUUM operation moving a row version"},{"c0":"t_ctid","c1":"ItemPointerData","c2":"6 bytes","c3":"current TID of this or newer row version"},{"c0":"t_infomask2","c1":"uint16","c2":"2 bytes","c3":"number of attributes, plus various flag bits"},{"c0":"t_infomask","c1":"uint16","c2":"2 bytes","c3":"various flag bits"},{"c0":"t_hoff","c1":"uint8","c2":"1 byte","c3":"offset to user data"}],"title":"HeapTupleHeaderData Layout"}]}},"RequestedLocale":"zh-Hans","Fallback":true,"Versions":["10","11","12","13","14","15","16","17","18","19","20"],"Locales":["en"],"Signatures":null,"Spellings":null,"SQLState":null,"Evidence":null}
