@@ -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