Skip to content

Commit 5cd7eb1

Browse files
Add various assertions to heap pruning code.
These assertions document (and verify) our high level assumptions about how pruning can and cannot affect existing items from target heap pages. For example, one of the new assertions verifies that pruning does not set a heap-only tuple to LP_DEAD. Author: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/CAH2-Wz=vhvBx1GjF+oueHh8YQcHoQYrMi0F0zFMHEr8yc4sCoA@mail.gmail.com
1 parent 9588622 commit 5cd7eb1

File tree

1 file changed

+80
-4
lines changed

1 file changed

+80
-4
lines changed

src/backend/access/heap/pruneheap.c

+80-4
Original file line numberDiff line numberDiff line change
@@ -844,39 +844,115 @@ heap_page_prune_execute(Buffer buffer,
844844
{
845845
Page page = (Page) BufferGetPage(buffer);
846846
OffsetNumber *offnum;
847-
int i;
847+
HeapTupleHeader htup PG_USED_FOR_ASSERTS_ONLY;
848848

849849
/* Shouldn't be called unless there's something to do */
850850
Assert(nredirected > 0 || ndead > 0 || nunused > 0);
851851

852852
/* Update all redirected line pointers */
853853
offnum = redirected;
854-
for (i = 0; i < nredirected; i++)
854+
for (int i = 0; i < nredirected; i++)
855855
{
856856
OffsetNumber fromoff = *offnum++;
857857
OffsetNumber tooff = *offnum++;
858858
ItemId fromlp = PageGetItemId(page, fromoff);
859+
ItemId tolp PG_USED_FOR_ASSERTS_ONLY;
860+
861+
#ifdef USE_ASSERT_CHECKING
862+
863+
/*
864+
* Any existing item that we set as an LP_REDIRECT (any 'from' item)
865+
* must be the first item from a HOT chain. If the item has tuple
866+
* storage then it can't be a heap-only tuple. Otherwise we are just
867+
* maintaining an existing LP_REDIRECT from an existing HOT chain that
868+
* has been pruned at least once before now.
869+
*/
870+
if (!ItemIdIsRedirected(fromlp))
871+
{
872+
Assert(ItemIdHasStorage(fromlp) && ItemIdIsNormal(fromlp));
873+
874+
htup = (HeapTupleHeader) PageGetItem(page, fromlp);
875+
Assert(!HeapTupleHeaderIsHeapOnly(htup));
876+
}
877+
else
878+
{
879+
/* We shouldn't need to redundantly set the redirect */
880+
Assert(ItemIdGetRedirect(fromlp) != tooff);
881+
}
882+
883+
/*
884+
* The item that we're about to set as an LP_REDIRECT (the 'from'
885+
* item) will point to an existing item (the 'to' item) that is
886+
* already a heap-only tuple. There can be at most one LP_REDIRECT
887+
* item per HOT chain.
888+
*
889+
* We need to keep around an LP_REDIRECT item (after original
890+
* non-heap-only root tuple gets pruned away) so that it's always
891+
* possible for VACUUM to easily figure out what TID to delete from
892+
* indexes when an entire HOT chain becomes dead. A heap-only tuple
893+
* can never become LP_DEAD; an LP_REDIRECT item or a regular heap
894+
* tuple can.
895+
*/
896+
tolp = PageGetItemId(page, tooff);
897+
Assert(ItemIdHasStorage(tolp) && ItemIdIsNormal(tolp));
898+
htup = (HeapTupleHeader) PageGetItem(page, tolp);
899+
Assert(HeapTupleHeaderIsHeapOnly(htup));
900+
#endif
859901

860902
ItemIdSetRedirect(fromlp, tooff);
861903
}
862904

863905
/* Update all now-dead line pointers */
864906
offnum = nowdead;
865-
for (i = 0; i < ndead; i++)
907+
for (int i = 0; i < ndead; i++)
866908
{
867909
OffsetNumber off = *offnum++;
868910
ItemId lp = PageGetItemId(page, off);
869911

912+
#ifdef USE_ASSERT_CHECKING
913+
914+
/*
915+
* An LP_DEAD line pointer must be left behind when the original item
916+
* (which is dead to everybody) could still be referenced by a TID in
917+
* an index. This should never be necessary with any individual
918+
* heap-only tuple item, though. (It's not clear how much of a problem
919+
* that would be, but there is no reason to allow it.)
920+
*/
921+
if (ItemIdHasStorage(lp))
922+
{
923+
Assert(ItemIdIsNormal(lp));
924+
htup = (HeapTupleHeader) PageGetItem(page, lp);
925+
Assert(!HeapTupleHeaderIsHeapOnly(htup));
926+
}
927+
else
928+
{
929+
/* Whole HOT chain becomes dead */
930+
Assert(ItemIdIsRedirected(lp));
931+
}
932+
#endif
933+
870934
ItemIdSetDead(lp);
871935
}
872936

873937
/* Update all now-unused line pointers */
874938
offnum = nowunused;
875-
for (i = 0; i < nunused; i++)
939+
for (int i = 0; i < nunused; i++)
876940
{
877941
OffsetNumber off = *offnum++;
878942
ItemId lp = PageGetItemId(page, off);
879943

944+
#ifdef USE_ASSERT_CHECKING
945+
946+
/*
947+
* Only heap-only tuples can become LP_UNUSED during pruning. They
948+
* don't need to be left in place as LP_DEAD items until VACUUM gets
949+
* around to doing index vacuuming.
950+
*/
951+
Assert(ItemIdHasStorage(lp) && ItemIdIsNormal(lp));
952+
htup = (HeapTupleHeader) PageGetItem(page, lp);
953+
Assert(HeapTupleHeaderIsHeapOnly(htup));
954+
#endif
955+
880956
ItemIdSetUnused(lp);
881957
}
882958

0 commit comments

Comments
 (0)