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. Any padding needed to make
<structfield>t_hoff</structfield> a MAXALIGN multiple will appear between the null
bitmap and the object ID. (This in turn ensures that the object ID is
suitably aligned.)
</para>
<table tocentry="1" id="heaptupleheaderdata-table">
<title>HeapTupleHeaderData Layout</title>
<titleabbrev>HeapTupleHeaderData Layout</titleabbrev>
<tgroup cols="4">
<thead>
<row>
<entry>Field</entry>
<entry>Type</entry>
<entry>Length</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>t_xmin</entry>
<entry>TransactionId</entry>
<entry>4 bytes</entry>
<entry>insert XID stamp</entry>
</row>
<row>
<entry>t_xmax</entry>
<entry>TransactionId</entry>
<entry>4 bytes</entry>
<entry>delete XID stamp</entry>
</row>
<row>
<entry>t_cid</entry>
<entry>CommandId</entry>
<entry>4 bytes</entry>
<entry>insert and/or delete CID stamp (overlays with t_xvac)</entry>
</row>
<row>
<entry>t_xvac</entry>
<entry>TransactionId</entry>
<entry>4 bytes</entry>
<entry>XID for VACUUM operation moving a row version</entry>
</row>
<row>
<entry>t_ctid</entry>
<entry>ItemPointerData</entry>
<entry>6 bytes</entry>
<entry>current TID of this or newer row version</entry>
</row>
<row>
<entry>t_infomask2</entry>
<entry>uint16</entry>
<entry>2 bytes</entry>
<entry>number of attributes, plus various flag bits</entry>
</row>
<row>
<entry>t_infomask</entry>
<entry>uint16</entry>
<entry>2 bytes</entry>
<entry>various flag bits</entry>
</row>
<row>
<entry>t_hoff</entry>
<entry>uint8</entry>
<entry>1 byte</entry>
<entry>offset to user data</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
All the details can be found in
<filename>src/include/access/htup_details.h</filename>.
</para>
<para>
Interpreting the actual data can only be done with information obtained
from other tables, mostly <structname>pg_attribute</structname>. The
key values needed to identify field locations are
<structfield>attlen</structfield> and <structfield>attalign</structfield>.
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
<firstterm>heap_getattr</firstterm>, <firstterm>fastgetattr</firstterm>
and <firstterm>heap_getsysattr</firstterm>.
</para>
<para>
To read the data you need to examine each attribute in turn. First check
whether the field is NULL according to the null bitmap.