@@ -446,6 +446,11 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
446446 scan -> rs_ntuples = 0 ;
447447 scan -> rs_cindex = 0 ;
448448
449+ /* Initialize scan-time pruning tracking */
450+ scan -> rs_page_updates = 0 ;
451+ scan -> rs_page_pruned = false;
452+ scan -> rs_last_pruned_block = InvalidBlockNumber ;
453+
449454 /*
450455 * Initialize to ForwardScanDirection because it is most common and
451456 * because heap scans go forward before going backward (e.g. CURSORs).
@@ -925,6 +930,10 @@ heapgettup(HeapScanDesc scan,
925930
926931 Assert (BufferGetBlockNumber (scan -> rs_cbuf ) == scan -> rs_cblock );
927932
933+ /* Reset page tracking for new page */
934+ scan -> rs_page_updates = 0 ;
935+ scan -> rs_page_pruned = false;
936+
928937 LockBuffer (scan -> rs_cbuf , BUFFER_LOCK_SHARE );
929938 page = heapgettup_start_page (scan , dir , & linesleft , & lineoff );
930939continue_page :
@@ -942,7 +951,12 @@ heapgettup(HeapScanDesc scan,
942951 ItemId lpp = PageGetItemId (page , lineoff );
943952
944953 if (!ItemIdIsNormal (lpp ))
954+ {
955+ /* Track dead line pointers as potential modifications */
956+ if (ItemIdIsDead (lpp ))
957+ scan -> rs_page_updates ++ ;
945958 continue ;
959+ }
946960
947961 tuple -> t_data = (HeapTupleHeader ) PageGetItem (page , lpp );
948962 tuple -> t_len = ItemIdGetLength (lpp );
@@ -972,10 +986,27 @@ heapgettup(HeapScanDesc scan,
972986 }
973987
974988 /*
975- * if we get here, it means we've exhausted the items on this page and
976- * it's time to move to the next.
989+ * Before moving to next page, check if current page needs scan-time
990+ * pruning. This addresses the issue where multiple updates during a
991+ * scan don't trigger pruning until the next scan.
977992 */
978993 LockBuffer (scan -> rs_cbuf , BUFFER_LOCK_UNLOCK );
994+ if (scan -> rs_page_updates > 0 &&
995+ scan -> rs_cblock != scan -> rs_last_pruned_block &&
996+ PageNeedsScanPruning (page , scan -> rs_page_updates ))
997+ {
998+ /* Attempt opportunistic pruning */
999+ heap_page_prune_opt (scan -> rs_base .rs_rd , scan -> rs_cbuf , 0 );
1000+
1001+ /* Mark this block as pruned to avoid repeated attempts */
1002+ scan -> rs_last_pruned_block = scan -> rs_cblock ;
1003+ scan -> rs_page_pruned = true;
1004+ }
1005+
1006+ /*
1007+ * If we get here, it means we've exhausted the items on this page and
1008+ * it's time to move to the next.
1009+ */
9791010 }
9801011
9811012 /* end of scan */
@@ -1042,6 +1073,10 @@ heapgettup_pagemode(HeapScanDesc scan,
10421073
10431074 Assert (BufferGetBlockNumber (scan -> rs_cbuf ) == scan -> rs_cblock );
10441075
1076+ /* Reset page tracking for new page */
1077+ scan -> rs_page_updates = 0 ;
1078+ scan -> rs_page_pruned = false;
1079+
10451080 /* prune the page and determine visible tuple offsets */
10461081 heap_prepare_pagescan ((TableScanDesc ) scan );
10471082 page = BufferGetPage (scan -> rs_cbuf );
@@ -1079,6 +1114,15 @@ heapgettup_pagemode(HeapScanDesc scan,
10791114 }
10801115 }
10811116
1117+ /* Before ending scan, check if current page needs scan-time pruning */
1118+ if (BufferIsValid (scan -> rs_cbuf ) && scan -> rs_page_updates > 0 &&
1119+ scan -> rs_cblock != scan -> rs_last_pruned_block &&
1120+ PageNeedsScanPruning (BufferGetPage (scan -> rs_cbuf ), scan -> rs_page_updates ))
1121+ {
1122+ heap_page_prune_opt (scan -> rs_base .rs_rd , scan -> rs_cbuf , 0 );
1123+ scan -> rs_last_pruned_block = scan -> rs_cblock ;
1124+ }
1125+
10821126 /* end of scan */
10831127 if (BufferIsValid (scan -> rs_cbuf ))
10841128 ReleaseBuffer (scan -> rs_cbuf );
0 commit comments