<entry>2 bytes</entry>
<entry>Offset to end of free space</entry>
</row>
<row>
<entry>pd_special</entry>
<entry>LocationIndex</entry>
<entry>2 bytes</entry>
<entry>Offset to start of special space</entry>
</row>
<row>
<entry>pd_pagesize_version</entry>
<entry>uint16</entry>
<entry>2 bytes</entry>
<entry>Page size and layout version number information</entry>
</row>
<row>
<entry>pd_prune_xid</entry>
<entry>TransactionId</entry>
<entry>4 bytes</entry>
<entry>Oldest unpruned XMAX on page, or zero if none</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
All the details can be found in
<filename>src/include/storage/bufpage.h</filename>.
</para>
<para>
Following the page header are item identifiers
(<type>ItemIdData</type>), 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
<structfield>pd_lower</structfield>, 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 (<type>ItemPointer</type>, also known as
<type>CTID</type>) created by
<productname>PostgreSQL</productname> consists of a page number and the
index of an item identifier.
</para>
<para>
The 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
<type>HeapTupleHeaderData</type>, described below.
</para>
<para>
The final section is the <quote>special section</quote> 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
<structfield>pd_special</structfield> to equal the page size).
</para>
<para>
<xref linkend="storage-page-layout-figure"/> illustrates how these parts are
laid out in a page.
</para>
<figure id="storage-page-layout-figure">
<title>Page Layout</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/pagelayout.svg" format="SVG" width="100%"/>
</imageobject>
</mediaobject>
</figure>
<sect2 id="storage-tuple-layout">
<title>Table Row Layout</title>
<para>
All 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 <xref linkend="heaptupleheaderdata-table"/>. The actual user data
(columns of the row) begins at the offset indicated by
<structfield>t_hoff</structfield>, which must always be a multiple of the MAXALIGN
distance for the platform.
The null bitmap is
only present if the <firstterm>HEAP_HASNULL</firstterm> bit is set in
<structfield>t_infomask</structfield>. 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
<structfield>t_infomask2</structfield>). 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 <firstterm>HEAP_HASOID_OLD</firstterm> bit
is set in <structfield>t_infomask</structfield>. If present, it appears just
before the <structfield>t_hoff</structfield> boundary.