PostgreSQL TOAST Moves Large Values Outside Heap Rows

PostgreSQL heap tuples cannot span data pages. A row containing a large text, bytea, jsonb, or other variable-length value therefore cannot simply continue onto the next heap page. TOAST provides the storage mechanism that keeps such rows representable: eligible values can be compressed, moved out of line, or both.

The visible SQL value does not change when this happens. The physical representation does. A heap tuple may contain a compact reference while the value’s bytes reside as chunk rows in a separate relation associated with the table.

Page boundaries drive the storage mechanism

PostgreSQL stores table data in fixed-size pages. The common build uses 8 KiB pages, and an individual heap tuple must fit inside one page. Large logical values need a representation that respects that physical boundary.

TOAST applies to variable-length data types whose representation supports the mechanism. When a tuple exceeds the configured threshold, PostgreSQL attempts to reduce its inline size according to each column’s storage strategy. Compression can make a value small enough to remain inline. If that is insufficient and the strategy permits it, PostgreSQL can replace the inline value with an out-of-line reference.

This is a row-width mechanism rather than a separate SQL data type. Applications continue to read and write the original column type.

Out-of-line values become ordered chunks

A table with TOAST-capable columns can have an associated TOAST relation. PostgreSQL records that relation through pg_class.reltoastrelid.

An out-of-line value is split into chunk rows after compression when compression is applicable. Each chunk carries an identifier for the value, a sequence number, and chunk data. A unique index on the value identifier and sequence number supports ordered retrieval.

The main heap tuple stores a pointer rather than the full payload. That pointer carries enough metadata to locate the external value and describe its logical and stored sizes. Fetching the column can therefore require access beyond the main heap relation.

This separation matters for row access. A query that does not need the large attribute can operate on the compact heap tuple without reconstructing the external payload merely because the row contains one.

Storage strategy changes compression and placement

TOAST-capable columns have a storage strategy. PostgreSQL exposes four strategies through ALTER TABLE ... SET STORAGE.

EXTENDED, the common default for TOAST-capable types, permits compression and out-of-line storage. PostgreSQL can first attempt compression and then move data out of line when row width still requires it.

EXTERNAL permits out-of-line storage without compression. This can preserve direct access patterns for operations that can fetch slices of uncompressed external text or bytea, at the cost of storing more bytes when the value would otherwise compress well.

MAIN favors inline storage and permits compression. External storage remains possible as a last resort when a tuple cannot otherwise fit.

PLAIN prevents both compression and out-of-line storage. It is also the required strategy for types that do not support TOAST.

These settings control physical representation, not a promise that every value will use one fixed form. Actual placement depends on tuple width, value size, compressibility, and the selected strategy.

Unchanged external values can survive an update

An update in PostgreSQL creates a new row version. That does not imply that every external value must be rebuilt.

When an out-of-line attribute is unchanged, PostgreSQL can preserve its existing external representation rather than reconstructing and writing the same chunk set again. This limits TOAST work for updates that modify other columns.

Changing the large attribute is different. A new value may need compression, chunk creation, and a new external representation. The old row version can remain visible to transactions whose snapshots still require it, so storage reclamation follows PostgreSQL’s normal MVCC and vacuum lifecycle rather than the timing of the SQL statement alone.

This distinction is useful when interpreting write amplification on tables with large payload columns. Row-version creation and large-value replacement are related operations, but they are not identical costs.

Logical size and heap size can diverge sharply

A row can represent a large logical payload while occupying far less space in the main heap. Compression may reduce the bytes stored, and external placement may reduce the heap representation to a pointer.

As a result, measuring only the heap relation can give an incomplete view of a table’s storage footprint. TOAST relation and index storage can hold a substantial share of the bytes for workloads dominated by large variable-length attributes.

The same split affects access cost. Scanning narrow columns from such a table does not necessarily require reading every external payload. Selecting or evaluating an external attribute can introduce additional reads and decompression work.

Physical row width, logical value size, and total relation storage are therefore separate quantities. TOAST is the mechanism that makes that separation possible while preserving ordinary SQL semantics.

A useful boundary is that TOAST solves representation of wide variable-length values within PostgreSQL’s page model; it does not make large payload access free. Schema and query design still determine whether those external bytes stay off the critical read path or must be reconstructed for each operation.