HOT Updates in PostgreSQL: Why an Indexed Column Change Can Still Stay on the Same Page

A user experimenting with heap-only tuple (HOT) updates in PostgreSQL ran into a case that looks like it should not qualify: the column being modified was part of an index, yet the row version did not move to a new block.

The test setup

The table carries a primary key on age, a separate btree index on name, autovacuum disabled, and a fillfactor of 70 percent. It was loaded with 235 rows generated by generate_series, each getting a name of the form My name-N.

Before any modification, the row for age 1 sits at ctid (0,1) with the name My name-1.

The unexpected result

The user then updates an indexed column:

update my_ages set "name" = 'My awesome name 1' where "name" = 'My name-1';

Querying the row back by age = 1, or by the new name value, returns ctid (0,110) — still within the same block. Querying by either the primary key or the name index produces the same result.

That leaves the question the user raises: is this a misunderstanding of how HOT works, or is something in the setup wrong?

Reply