@@ -688,22 +688,14 @@ compactify_tuples(itemIdCompact itemidbase, int nitems, Page page, bool presorte
688688 *
689689 * This routine is usable for heap pages only, but see PageIndexMultiDelete.
690690 *
691- * Never removes unused line pointers. PageTruncateLinePointerArray can
692- * safely remove some unused line pointers. It ought to be safe for this
693- * routine to free unused line pointers in roughly the same way, but it's not
694- * clear that that would be beneficial.
695- *
696- * PageTruncateLinePointerArray is only called during VACUUM's second pass
697- * over the heap. Any unused line pointers that it sees are likely to have
698- * been set to LP_UNUSED (from LP_DEAD) immediately before the time it is
699- * called. On the other hand, many tables have the vast majority of all
700- * required pruning performed opportunistically (not during VACUUM). And so
701- * there is, in general, a good chance that even large groups of unused line
702- * pointers that we see here will be recycled quickly.
691+ * This routine removes unused line pointers from the end of the line pointer
692+ * array. This is possible when dead heap-only tuples get removed by pruning,
693+ * especially when there were HOT chains with several tuples each beforehand.
703694 *
704695 * Caller had better have a full cleanup lock on page's buffer. As a side
705696 * effect the page's PD_HAS_FREE_LINES hint bit will be set or unset as
706- * needed.
697+ * needed. Caller might also need to account for a reduction in the length of
698+ * the line pointer array following array truncation.
707699 */
708700void
709701PageRepairFragmentation (Page page )
@@ -718,6 +710,7 @@ PageRepairFragmentation(Page page)
718710 int nline ,
719711 nstorage ,
720712 nunused ;
713+ OffsetNumber finalusedlp = InvalidOffsetNumber ;
721714 int i ;
722715 Size totallen ;
723716 bool presorted = true; /* For now */
@@ -771,10 +764,13 @@ PageRepairFragmentation(Page page)
771764 totallen += itemidptr -> alignedlen ;
772765 itemidptr ++ ;
773766 }
767+
768+ finalusedlp = i ; /* Could be the final non-LP_UNUSED item */
774769 }
775770 else
776771 {
777772 /* Unused entries should have lp_len = 0, but make sure */
773+ Assert (!ItemIdHasStorage (lp ));
778774 ItemIdSetUnused (lp );
779775 nunused ++ ;
780776 }
@@ -798,6 +794,19 @@ PageRepairFragmentation(Page page)
798794 compactify_tuples (itemidbase , nstorage , page , presorted );
799795 }
800796
797+ if (finalusedlp != nline )
798+ {
799+ /* The last line pointer is not the last used line pointer */
800+ int nunusedend = nline - finalusedlp ;
801+
802+ Assert (nunused >= nunusedend && nunusedend > 0 );
803+
804+ /* remove trailing unused line pointers from the count */
805+ nunused -= nunusedend ;
806+ /* truncate the line pointer array */
807+ ((PageHeader ) page )-> pd_lower -= (sizeof (ItemIdData ) * nunusedend );
808+ }
809+
801810 /* Set hint bit for PageAddItemExtended */
802811 if (nunused > 0 )
803812 PageSetHasFreeLinePointers (page );
0 commit comments