Wiki / Storage Structures / Physical structures
Table row layout
Physical row headers, null bitmap, alignment and column data.
Reading PostgreSQL 18.6.
Description
Physical row headers, null bitmap, alignment and column data.
HeapTupleHeaderData Layout
| Field | Type | Length | Description |
|---|---|---|---|
| t_xmin | TransactionId | 4 bytes | insert XID stamp |
| t_xmax | TransactionId | 4 bytes | delete XID stamp |
| t_cid | CommandId | 4 bytes | insert and/or delete CID stamp (overlays with t_xvac) |
| t_xvac | TransactionId | 4 bytes | XID for VACUUM operation moving a row version |
| t_ctid | ItemPointerData | 6 bytes | current TID of this or newer row version |
| t_infomask2 | uint16 | 2 bytes | number of attributes, plus various flag bits |
| t_infomask | uint16 | 2 bytes | various flag bits |
| t_hoff | uint8 | 1 byte | offset to user data |
Manual definition
66.6.1. Table Row Layout
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 Table 66.4. The actual user data (columns of the row) begins at the offset indicated by t_hoff, which must always be a multiple of the MAXALIGN distance for the platform. The null bitmap is only present if the HEAP_HASNULL bit is set in t_infomask. 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 t_infomask2). 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 HEAP_HASOID_OLD bit is set in t_infomask. If present, it appears just before the t_hoff boundary. Any padding needed to make t_hoff a MAXALIGN multiple will appear between the null bitmap and the object ID. (This in turn ensures that the object ID is suitably aligned.)
Table 66.4. HeapTupleHeaderData Layout
| Field | Type | Length | Description |
|---|---|---|---|
| t_xmin | TransactionId | 4 bytes | insert XID stamp |
| t_xmax | TransactionId | 4 bytes | delete XID stamp |
| t_cid | CommandId | 4 bytes | insert and/or delete CID stamp (overlays with t_xvac) |
| t_xvac | TransactionId | 4 bytes | XID for VACUUM operation moving a row version |
| t_ctid | ItemPointerData | 6 bytes | current TID of this or newer row version |
| t_infomask2 | uint16 | 2 bytes | number of attributes, plus various flag bits |
| t_infomask | uint16 | 2 bytes | various flag bits |
| t_hoff | uint8 | 1 byte | offset to user data |
All the details can be found in src/include/access/htup_details.h.
Interpreting the actual data can only be done with information obtained from other tables, mostly pg_attribute. The key values needed to identify field locations are attlen and attalign. 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 heap_getattr, fastgetattr and heap_getsysattr.
To 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 struct varlena, 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 Section 66.2).
Documentation and source
Source build
- Version
- 18.6
- Build
- https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2
- Source fingerprint
555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f
Compare versions
PostgreSQL 10 → 11: added.
--- PostgreSQL 10
+++ PostgreSQL 11
@@ -1 +1,76 @@
-Not recorded in this version
+{
+ "layouts": [
+ {
+ "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"
+ }
+ ]
+}
Compares recorded interfaces and attributes. Source fingerprints and build metadata are excluded; an absent sample is not proof of the introduction or removal release.
Related entries
Export JSON · Back to Storage Structures · Recorded in PostgreSQL 11 through 20; the first sample is not necessarily its introduction.