You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
tsl/compression: fix stale sparse-index entries after rebuild_sparse_index
populate_sparse_index_columns() rewrites each compressed batch's sparse
index metadata columns (min/max, first/last, bloom) in place via
simple_table_tuple_update(), but discarded the TU_UpdateIndexes result
that call computes instead of acting on it. When the rewrite wasn't
HOT-eligible (typically because the row no longer fits on its
original page), the btree index covering those columns never got a
new entry for the row's new location, and the stale entry pointing at
the superseded tuple was left behind.
The stale entry usually goes unnoticed because PostgreSQL's HOT-chain
"redirect" mechanism transparently follows it to the live tuple, as
long as the chain stays on the same page. Once that chain is broken --
by ordinary heap pruning/vacuum, or whenever the rewrite must relocate
to a different page -- an index or bitmap scan using the stale entry
silently returns the wrong (or zero) rows for the affected batch,
while a sequential scan still returns the correct data. This can
surface as wrong query results for compressed hypertables after
compress_index changes (e.g. via rebuild_sparse_index() or an
ALTER TABLE compress_index change followed by recompression).
Fix by opening the compressed table's indexes once per
populate_sparse_index_columns() call and inserting the new entry
whenever the update actually wrote a new tuple, mirroring the same
open-index/simple-update/insert-index-entry sequence PostgreSQL's own
CatalogTupleUpdate() uses for catalog tuples. This requires switching
from simple_table_tuple_update() to simple_heap_update(): the former
funnels the update through a TupleTableSlot and materializes a
throwaway copy of the tuple inside heapam_tuple_update(), so the
caller's own HeapTuple is never updated with the tuple's real
post-update location or HOT status. simple_heap_update() operates
directly on the passed HeapTuple, exactly as CatalogTupleUpdate()
relies on -- consistent with the rest of this function, which already
assumes a plain heap-backed relation (heap_modify_tuple,
heap_deform_tuple).
Adds a regression check that forces an index/bitmap scan after the
rebuild_sparse_index() drop-and-restore dance in Test 10 and confirms
per-device batch counts match a sequential scan; previously this
would return fewer rows once the affected batch's index entry had
gone stale.
0 commit comments